Branch protection rules not enforced on Windows — hooks not executing (pre-receive ignored)

Description

Branch protection rules are completely ineffective on our Gitea instance running on Windows. Any user, including non-admin users, can push directly to protected branches without any error or rejection.

Environment

  • Gitea version: 1.25.4 (upgraded from 1.22.6)

  • OS: Windows Server (Windows binary installation, not Docker)

  • Git client: Visual Studio built-in Git client + GitHub Desktop

  • Protocol: HTTP

Configuration

Branch protection rule on main:

  • Push: Disabled (no direct push allowed)

  • Required approvals: 1

  • Revoke stale approvals: enabled

  • Block merge on rejected reviews: enabled

  • Administrators must follow branch protection rules: enabled

Steps to reproduce

  1. Create a branch protection rule on main with push disabled

  2. Push directly to main from any Git client (Visual Studio, GitHub Desktop) as any user (admin or non-admin)

  3. Push succeeds with no error message whatsoever

Investigation

We inspected the hooks directory at: C:\gitea\data\gitea-repositories\<org>\<repo>.git\hooks\

The hooks are present (pre-receive, post-receive, proc-receive, update) and were regenerated via the “Resync git hooks” admin task. The pre-receive.d\gitea file contains the correct auto-generated content:

bash

#!/usr/bin/env bash
# AUTO GENERATED BY GITEA, DO NOT MODIFY
C:/gitea/gitea.exe hook --config="C:\\gitea\\custom\\conf\\app.ini" pre-receive

The root pre-receive file contains:

bash

#!/usr/bin/env bash
# AUTO GENERATED BY GITEA, DO NOT MODIFY
data=$(cat)
exitcodes=""
hookname=$(basename $0)
GIT_DIR=${GIT_DIR:-$(dirname $0)/..}
for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do
  test -x "${hook}" && test -f "${hook}" || continue
  echo "${data}" | "${hook}"
  exitcodes="${exitcodes} $?"
done
for i in ${exitcodes}; do
  [ ${i} -eq 0 ] || exit ${i}
done

We noticed that test -x fails on Windows because files do not have Unix executable bits. We ran chmod +x on all hook files via Git Bash:

bash

chmod +x pre-receive pre-receive.d/gitea
chmod +x post-receive post-receive.d/gitea
chmod +x proc-receive proc-receive.d/gitea
chmod +x update update.d/gitea

This did not resolve the issue. Pushes to protected branches are still accepted silently.

Expected behavior

Any direct push to a protected branch should be rejected with an error message such as: remote: Gitea: branch main is protected

Actual behavior

Push succeeds silently. No error, no rejection, no hook output visible on the client side.

Additional notes

  • ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET is not set in app.ini (default value applies)

  • DISABLE_GIT_HOOKS is not set in app.ini (default value applies)

  • The issue affects all users regardless of admin status

  • Branch protection rules are correctly saved and visible in the repository settings UI

  • The problem appeared to exist on 1.22.6 as well (we upgraded hoping it would fix it)

Question

Is branch protection via server-side hooks fully supported on Windows binary installations? Is there a known workaround or required configuration for Windows environments?