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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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