What happens
git push origin --delete <branch> against a gitlawb remote never returns. I killed it at five minutes; there is no error, no timeout, no output — it simply sits there.
Reproduced against https://origin.gitlawb.com with git-remote-gitlawb built from feat/ucan-push-authorization, git 2.54.0.windows.1:
git push origin probe/slash-test:refs/heads/probe/slash-test # succeeds
git push origin --delete probe/slash-test # hangs forever
The branch creation immediately before it worked, so this is specific to deletion rather than to the remote, the auth, or the ref name.
Why (likely)
handle_connect reads the entire receive-pack request with read_to_end on stdin, and the comment above it states the assumption directly (crates/git-remote-gitlawb/src/main.rs:294-296):
git-receive-pack (push): Git sends ref-update commands + the complete PACK blob, then closes its write pipe. read_to_end is safe and correct here, a single POST.
That assumption holds for a push that carries objects. A delete-only push carries none — git sends the ref-update command (<old-oid> 0000000…0 refs/heads/x) plus a flush, and no pack follows, because there is nothing to transfer. If git then holds its write pipe open waiting for the report-status response before closing it, read_to_end waits for an EOF that only arrives after a response that is never sent, and the two deadlock.
I have not instrumented this to prove it, so treat the mechanism as a strong hypothesis rather than a finding. The hang itself is reproducible.
Why it matters
Deleting a remote branch is routine — merged feature branches, cleaning up after CI, tidying a fork. Today it does not fail, which would at least be honest; it hangs, so a script doing it has no natural recovery and a person has to notice and interrupt it.
Suggested direction
Stop depending on EOF to know the request is complete: parse the pkt-line stream and stop at the flush packet that ends the command list, then decide whether a pack follows based on whether any command has a non-zero new-oid. negotiate_upload_pack already does per-round pkt-line framing rather than reading to EOF (read_exact on the 4-byte length, flush handling at main.rs:671-701), so the machinery exists in this file.
A test that drives handle_connect with a delete-only command stream and asserts it returns would pin it. The existing harness at main.rs:1367 already feeds a synthetic stdin cursor.
Environment
git-remote-gitlawb @ fad75a6 (also present on main — this code is untouched by open PRs)
- git 2.54.0.windows.1, Windows 11
- node
origin.gitlawb.com, v0.7.1
What happens
git push origin --delete <branch>against a gitlawb remote never returns. I killed it at five minutes; there is no error, no timeout, no output — it simply sits there.Reproduced against
https://origin.gitlawb.comwithgit-remote-gitlawbbuilt fromfeat/ucan-push-authorization, git 2.54.0.windows.1:The branch creation immediately before it worked, so this is specific to deletion rather than to the remote, the auth, or the ref name.
Why (likely)
handle_connectreads the entire receive-pack request withread_to_endon stdin, and the comment above it states the assumption directly (crates/git-remote-gitlawb/src/main.rs:294-296):That assumption holds for a push that carries objects. A delete-only push carries none — git sends the ref-update command (
<old-oid> 0000000…0 refs/heads/x) plus a flush, and no pack follows, because there is nothing to transfer. If git then holds its write pipe open waiting for the report-status response before closing it,read_to_endwaits for an EOF that only arrives after a response that is never sent, and the two deadlock.I have not instrumented this to prove it, so treat the mechanism as a strong hypothesis rather than a finding. The hang itself is reproducible.
Why it matters
Deleting a remote branch is routine — merged feature branches, cleaning up after CI, tidying a fork. Today it does not fail, which would at least be honest; it hangs, so a script doing it has no natural recovery and a person has to notice and interrupt it.
Suggested direction
Stop depending on EOF to know the request is complete: parse the pkt-line stream and stop at the flush packet that ends the command list, then decide whether a pack follows based on whether any command has a non-zero new-oid.
negotiate_upload_packalready does per-round pkt-line framing rather than reading to EOF (read_exacton the 4-byte length, flush handling atmain.rs:671-701), so the machinery exists in this file.A test that drives
handle_connectwith a delete-only command stream and asserts it returns would pin it. The existing harness atmain.rs:1367already feeds a synthetic stdin cursor.Environment
git-remote-gitlawb@fad75a6(also present onmain— this code is untouched by open PRs)origin.gitlawb.com, v0.7.1