Topic: How to fix 'error: failed to push some refs (pre-receive hook declined)'
Category: DevOps • Collection: Git
This error means the remote server ran a pre-receive hook and rejected your push. It’s not a network glitch or a local Git bug—policy enforcement on the server blocked the update. Common causes include protected branches, non–fast-forward updates, commit policy checks, file size limits, or permission rules.
Quickstart (TL;DR)
- Read the server’s rejection message shown after the push; it often states the exact rule violated.
- If pushing to a protected branch (e.g., main): push to a new branch and open a pull request.
- If non–fast-forward: fetch, rebase or merge to align with remote, then push with --force-with-lease only if your team allows history rewrites.
- If large files or LFS required: move large binaries to Git LFS and purge them from history.
- If commit signing/policy failed: fix the commit(s) and push again.
Minimal working example (common fix)
Scenario: You’re trying to push changes that are behind the remote branch or the branch is protected.
# 1) Sync local refs
git fetch --prune origin
# 2) Update your feature branch with the latest main (adjust names as needed)
# If you were on main and it's protected, switch to a feature branch
BR=feature/fix-pre-receive-decline
git switch -c "$BR"
git rebase origin/main
# 3) Push a new branch to avoid protected-branch policies
git push --set-upstream origin "$BR"
# 4) Open a pull request on your platform to merge into main
If your team allows history rewrites and you must update an existing branch:
# Align your branch, then force-push safely
git fetch origin
git rebase origin/your-branch
# Use --force-with-lease (safer than --force)
git push --force-with-lease origin your-branch
Diagnose the rejection message
- The lines printed by the remote during push are key. Look for phrases like:
- "protected branch"
- "non-fast-forward updates are not allowed"
- "commit message does not match policy"
- "large file detected / LFS required"
- "missing required signature" or "unsigned commits"
- "secret detected" or "blocked file path"
- If you control the server, check the repository’s pre-receive hook and logs. On self-hosted Git, hooks live in repo.git/hooks/pre-receive.
- For deeper client-side tracing when needed:
GIT_TRACE=1 GIT_TRACE_PACKET=1 git push origin HEAD
Common causes and fixes
Protected branch rules
- Symptom: Pushing to main/develop is rejected.
- Fix: Push to a feature branch and create a pull request; or relax protection if policy allows.
Non–fast-forward update (history rewrite blocked)
- Symptom: "non-fast-forward" in message.
- Fix:
git fetch, thengit rebase origin/<branch>orgit merge origin/<branch>. If policy allows,git push --force-with-lease.
Missing permissions
- Symptom: You can read but cannot push to the branch.
- Fix: Request push/maintainer rights or push to a fork/feature branch.
Commit message or format policy
- Symptom: Messages like "must reference a ticket", "no merge commits", or "DCO sign-off required".
- Fix: Amend commits to meet policy:
git commit --amend -m "feat: JIRA-123 add validation"
# Or add DCO sign-off
git commit --amend -s
# Then push (force-with-lease if history changed)
git push --force-with-lease
- GPG/SSO signing required
- Symptom: "unsigned commits" or "invalid signature".
- Fix: Configure signing and re-create commits:
git config --global user.signingkey <KEYID>
git config --global commit.gpgsign true
# Rebase to re-sign each commit
GIT_COMMITTER_DATE="now" git rebase -i --rebase-merges --exec 'git commit --amend --no-edit --gpg-sign' origin/main
- Large files or Git LFS requirement
- Symptom: Errors mentioning file size or LFS.
- Fix: Track and migrate with LFS, then purge large blobs from history:
git lfs track "*.bin"
echo "*.bin" >> .gitattributes
# Migrate history to move existing large files to LFS
git lfs migrate import --include="*.bin"
# Force-push with lease if policy allows
git push --force-with-lease
- Secret scanning / forbidden content
- Symptom: "secret detected" or blocked patterns.
- Fix: Rotate secrets, remove from history, add secure config:
# Remove secret from latest commit
git reset --soft HEAD~1
# Replace with env var/config, recommit
# To scrub from history (careful):
BLOB=<path>
git filter-repo --path "$BLOB" --invert-paths
- Shallow clone limitations
- Symptom: Rebase fails due to missing history.
- Fix: Unshallow before rebasing:
git fetch --unshallow
Step-by-step resolution checklist
- Read the exact server message after the failed push.
- Identify the category: protection, non–fast-forward, permissions, policy, size/LFS, secrets.
- Synchronize local state:
git fetch --prune origin. - Update your branch:
git rebase origin/<target>(orgit mergeif your workflow prefers it). - If branch is protected: push to a new branch:
git push -u origin HEADand open a PR. - If policy failed: amend/rewrite commits to comply (message format, sign-off, GPG signing).
- If large files: adopt LFS and purge large blobs, then push.
- If history rewrite is intended and allowed:
git push --force-with-lease. - If you lack permission: request access or use a fork.
Pitfalls
- Using --force indiscriminately: prefer --force-with-lease to avoid overwriting collaborators’ work.
- Rewriting shared branch history without team agreement.
- Ignoring the server’s error text; it typically names the violation.
- Mixing binaries into Git history; use LFS early to avoid costly purges.
- Amending old commits without re-pushing all dependent branches.
Performance notes
- Large pushes are slow and more likely to trip hooks. Keep histories lean; run
git gcperiodically. - Use Git LFS for big assets; it reduces pack sizes and server-side checks on blobs.
- Rebasing a long-lived branch frequently keeps diffs small and pushes faster.
- Prefer
--force-with-leaseover--force; it reduces accidental retries from conflicts. - If CI runs as part of pre-receive on self-hosted setups, expect pushes to wait for checks; push smaller, incremental commits to shorten cycles.
Tiny FAQ
What is a pre-receive hook?
- A server-side script that runs before the remote accepts your updates. It can reject pushes that violate policy.
Can I bypass it with --no-verify?
- No. --no-verify only skips client-side hooks. Server hooks still run.
Why did push fail but pull/merge requests work?
- Pushing to protected branches is blocked; merging via PR runs checks and uses server-side permissions.
Is --force safe?
- Use --force-with-lease. It refuses to overwrite remote changes you haven’t seen.
I don’t control the server—what now?
- Adjust your workflow (feature branches, PRs) or contact the repo admin to review policies and permissions.