Volume XXII, number 280Wednesday, October 7, 2026Latest message 52 minutes ago

The Git List

News and archive of git@vger.kernel.org, since April 2005

receive-pack hangs on zero-object push into promisor-shaped repository

2 messages between Jun 29, 2026 and Jun 29, 2026, from Wei Hu, Patrick Steinhardt.

Plain Markdown or JSON for tools and agents.

Wei HuJun 29, 2026, 07:10 UTC on lore
Hello,

I found a reproducible hang in `git receive-pack` when pushing a ref update that sends zero objects into a repository that has promisor remote configuration and `.promisor` pack sidecar files.

The same zero-object ref update returns normally when the receiving repository is a normal non-bare repository or a bare repository. It also returns normally if I remove either the promisor remote config or the `.promisor` sidecar files from the receiving repository.

Check the attached script to reproduce the bug.
Environment:
  git version 2.54.0
  cpu: x86_64
  no commit associated with this build
  sizeof-long: 8
  sizeof-size_t: 8
  shell-path: /bin/sh
  rust: disabled
  gettext: enabled
  libcurl: 8.5.0
  zlib: 1.3
  SHA-1: SHA1_DC
  SHA-256: SHA256_BLK
  default-ref-format: files
  default-hash: sha1
  OS: Ubuntu 24.04.4 LTS (Noble Numbat)

#!/bin/sh set -eu

GIT=${GIT:-git} ROOT=$(mktemp -d "${TMPDIR:-/tmp}/git-promisor-push-hang.XXXXXX") SRC=$ROOT/src DST=$ROOT/dst UPSTREAM=$ROOT/upstream.git TRACE=$ROOT/trace.log

echo "root: $ROOT" echo "git: $($GIT --version)"

$GIT init -q "$SRC" $GIT -C "$SRC" config user.name Repro $GIT -C "$SRC" config user.email repro@example.invalid

printf A >"$SRC/file" $GIT -C "$SRC" add file $GIT -C "$SRC" commit -q -m A $GIT -C "$SRC" branch topic OLD=$($GIT -C "$SRC" rev-parse topic)

printf B >"$SRC/file" $GIT -C "$SRC" commit -q -am B $GIT -C "$SRC" branch -M main NEW=$($GIT -C "$SRC" rev-parse main)

$GIT clone -q --bare "$SRC" "$UPSTREAM" $GIT init -q "$DST" $GIT -C "$DST" config receive.denycurrentbranch updateInstead $GIT -C "$SRC" push -q "$DST" main:main topic:topic $GIT -C "$DST" checkout -q main

$GIT -C "$DST" config remote.origin.url "file://$UPSTREAM"
$GIT -C "$DST" config remote.origin.promisor true
$GIT -C "$DST" config remote.origin.partialclonefilter blob:none
$GIT -C "$DST" gc -q
for pack in "$DST"/.git/objects/pack/*.pack
do
	: >"${pack%.pack}.promisor"
done
$GIT -C "$DST" update-ref refs/heads/topic "$OLD"
status=0
timeout --kill-after=2s 8 \
	env GIT_TRACE=1 GIT_TRACE_PACKET=1 \
	$GIT -C "$SRC" push --porcelain "$DST" HEAD:topic \
	>"$TRACE" 2>&1 || status=$?

AFTER=$($GIT -C "$DST" rev-parse refs/heads/topic) ZERO_PACK=no grep -q -- '--pack_header=2,0' "$TRACE" && ZERO_PACK=yes

echo "old: $OLD" echo "new: $NEW" echo "after: $AFTER" echo "push status: $status" echo "zero-object pack observed: $ZERO_PACK" echo "trace: $TRACE"

if test "$status" = 124 && test "$AFTER" = "$OLD" && test "$ZERO_PACK" = yes
then
	echo "BUG REPRODUCED"
	exit 0
fi

echo "BUG NOT REPRODUCED" exit 1

Patrick SteinhardtJun 29, 2026, 15:30 UTC in reply to Wei Hu on lore

Re: receive-pack hangs on zero-object push into promisor-shaped repository

On Mon, Jun 29, 2026 at 03:10:42PM +0800, Wei Hu wrote:
Show 12 quoted lines
> Hello,
> 
> I found a reproducible hang in `git receive-pack` when pushing a ref update
> that sends zero objects into a repository that has promisor remote
> configuration and `.promisor` pack sidecar files.
> 
> The same zero-object ref update returns normally when the receiving
> repository is a normal non-bare repository or a bare repository. It
> also returns normally if I remove either the promisor remote config or
> the `.promisor` sidecar files from the receiving repository.
> 
> Check the attached script to reproduce the bug.

Thanks for your report! I was able to reduce your test case to the following minimal reproducer:

diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 1c2805acca..2850e78e49 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -723,6 +723,28 @@ test_expect_success 'after fetching descendants of non-promisor commits, gc work
 	git -C partial gc --prune=now
 '
 
+test_expect_success 'zero-object push does not hang' '
+	rm -rf src dst &&
+
+	git init src &&
+	test_commit -C src initial &&
+
+	git init --bare dst &&
+	git -C src push "$(pwd)/dst" main &&
+	git -C dst config set remote.origin.promisor true &&
+	git -C dst maintenance run &&
+	for pack in dst/objects/pack/*.pack
+	do
+		>"${pack%.pack}.promisor" || return 1
+	done &&
+
+	# Push the already-existing commit with a new branch name, which
+	# results in zero objects being written. This used to hang in the past.
+	git -C src push "$(pwd)/dst" main:topic &&
+	git -C src rev-parse main >expect &&
+	git -C dst rev-parse topic >actual &&
+	test_cmp expect actual
+'
 
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd

As it turns out, the bug itself was fixed already via d9982e8290
(connected: close err_fd in promisor fast-path, 2026-05-15), and that
fix is going to be part of Git 2.55 (which is due today).

That commit didn't add a test for this scenario though, even though the
commit message points out that there's been multiple regressions in this
area already. It's probably worth it to add the above test to our test
suite. Is this something you'd like to do? Otherwise I'm happy to send a
patch.

Thanks!

Patrick

Back to recent threads