Subject: [BUG] push: pre-push hook that waits for stdin is slow

From: "Kristoffer Haugsbakk" <[email-masked]>
Date: Tue, 13 Jan 2026 21:26:45 +0100

Thank you for filling out a Git bug report!
  Please answer the following questions to help us understand your issue.

  What did you do before the bug happened? (Steps to reproduce your issue)

Used `git push` with a pre-push hook which included a loop over standard
input (stdin):

```
#!/usr/bin/env bash

while read -r -a array
do
    # what the loop body does is not important
    :
done
```

At first I tested with a loop body. But I got the same behavior with
`:`.

It’s just the usual: check if what we are about to push is to some
blessed branch. If it is it says that the push failed and tells the user
to use `--no-verify` if they are sure.

  What did you expect to happen? (Expected behavior)

For the hook to finish quickly and the push to finish within a few
seconds at most.

  What happened instead? (Actual behavior)

The push to the remote (Bitbucket cloud) takes over a minute. But not
with `git push --no-verify`. Then it takes a few seconds.

  What's different between what you expected and what actually happened?

Time.

  Anything else you want to add:

I bisected using `timeout 3 git push ...` with a local remote to
857f047e (hook: allow overriding the ungroup option, 2025-12-26). I used
a script on my own $dayjob repository. But I also tested the following
script on commit f0f19d01 (fails) and the commit right before
it (does not fail).

It uses a pre-push hook which is just `cat`. Apparently it has to do
with how stdin is handled. An empty script does not have this problem.
And neither does a script that just uses `echo` (example).

```
#!/bin/sh

uuid=d0befd7e-efe4-11f0-b6e1-8b95c76aee96
repo="$HOME"/"$uuid"

# Destroy test stuff
cleanup () {
   rm -rf "$repo"
}

setup () {
    # I seem to need a realistic/meaty repo
    git clone . "$repo" &&
    (
        cd "$repo" &&
        git tag test-tag &&
        remote=b896ac6c-efe7-11f0-bebc-3bf1b96c1ded
        git clone . "$remote" &&
        git remote add local "$remote" &&
        # hook setup
        mkdir hooks &&
        cat <<-\EOF >hooks/pre-push &&
#!/bin/sh
cat
EOF
        chmod +x hooks/pre-push
    )
}

if test "$1" = cleanup
then
    cleanup
    echo >&2 "bisect: cleaned up test data"
    exit 128
fi &&

if ! test -e "$repo"
then
    setup
fi &&

make || exit 125

timeout 3 \
    ./bin-wrappers/git -C "$repo" -c core.hookspath=hooks \
    push local test-tag

# exit code 124: timeout(1) timed out
if test "$?" = 124
then
    exit 1
else
    exit 0
fi
```

I tested and reproduced the issue on `master`, `next`, and `seen`:

• master: on 8745eae5 (The 17th batch, 2026-01-11)
• next: 054afa95 (Sync with 'master', 2026-01-12)
• seen: b351b516 (Merge branch 'bc/sha1-256-interop-02' into seen,
  2026-01-12)

  Please review the rest of the bug report below.
  You can delete any lines you don't wish to share.

[System Info]
git version:
git version 2.52.0.421.gc32ead4fc78
cpu: x86_64
built from commit: c32ead4fc78dfd0327f6b2599e4fdca8166afa18
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
rust: disabled
libcurl: 7.81.0
OpenSSL: OpenSSL 3.0.2 15 Mar 2022
zlib: 1.2.11
SHA-1: SHA1_DC
SHA-256: SHA256_BLK
default-ref-format: files
default-hash: sha1
uname: Linux 6.8.0-90-generic #91~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 15:20:45 UTC 2 x86_64
compiler info: gnuc: 11.4
libc info: glibc: 2.35
$SHELL (typically, interactive shell): /bin/bash

[Enabled Hooks]
commit-msg
post-applypatch
post-checkout
post-commit
post-merge
post-rewrite
pre-auto-gc
pre-push
reference-transaction

-- 
Kristoffer