Skip to content

git push --delete hangs forever: the helper reads receive-pack to EOF, but a delete-only push sends no pack #369

Description

@Vasanthdev2004

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcrate:git-remotegit-remote-gitlawb — the git remote helperkind:bugDefect fix — wrong or unsafe behaviorsev:mediumDegraded but workaround existssubsystem:apiNode REST API request/response surface

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions