Git hooks are shell scripts that Git executes automatically at specific points in the workflow — before a commit (pre-commit), after a push (post-receive), before a push (pre-push), and so on. For WordPress deployments, the most powerful hook is post-receive on the remote (server-side) repository: every time you git push from your local machine, the server automatically checks out the latest code to the live webroot without any manual SSH login. This turns a three-step deployment (SSH into server, cd to site, git pull) into a single git push origin production. The setup requires a bare Git repository on the server (a repo with no working tree, used purely as a remote), a post-receive hook that checks out the pushed code to your WordPress root, and correct file permissions. Client-side hooks (pre-commit, pre-push) live in the .git/hooks/ directory and are not committed to the repository — use a tool like Husky for shareable team hooks. This approach is a step up from manual deployments but a step below a full CI/CD pipeline — it is ideal for solo developers and small teams managing WordPress on a VPS. Combine it with the Gitflow branching guide and automated backups run before each deploy for a complete workflow.
Problem: Deploying WordPress theme and plugin changes requires manually SSHing into the server and running git pull every time, which is error-prone and slow.
Solution: Set up a bare Git repository and post-receive hook on your server:
# ── On the server: create a bare repository ──────────────────────────────────
mkdir -p /var/git/helloadmin.git
cd /var/git/helloadmin.git
git init --bare
# ── Create the post-receive hook ─────────────────────────────────────────────
cat > /var/git/helloadmin.git/hooks/post-receive << 'EOF'
#!/bin/bash
# Deploy to WordPress root on push to 'main' branch
SITE_ROOT="/var/www/html/helloadmin"
BRANCH="main"
while read oldrev newrev refname; do
if [ "$refname" = "refs/heads/$BRANCH" ]; then
echo "=> Deploying branch '$BRANCH' to $SITE_ROOT ..."
git --work-tree="$SITE_ROOT" --git-dir="/var/git/helloadmin.git" checkout -f "$BRANCH"
# Set correct permissions (adjust www-data to your web server user)
chown -R www-data:www-data "$SITE_ROOT/wp-content/themes/"
chown -R www-data:www-data "$SITE_ROOT/wp-content/plugins/"
echo "=> Deploy complete."
fi
done
EOF
chmod +x /var/git/helloadmin.git/hooks/post-receive
# ── On your local machine: add the server as a remote ────────────────────────
git remote add production ssh://user@your-server-ip/var/git/helloadmin.git
# ── Deploy by pushing ─────────────────────────────────────────────────────────
git push production main
# ── Client-side pre-push hook (optional): run syntax check before pushing ────
# File: .git/hooks/pre-push (chmod +x)
# #!/bin/bash
# php -l wp-content/themes/your-theme/*.php
# if [ $? -ne 0 ]; then echo "PHP syntax error — push aborted."; exit 1; fi
NOTE: The post-receive hook runs as the Git user on the server, which may not have write permissions to the webroot. Either run the hook as the web server user (www-data) or add the Git user to the web server’s group and set directory permissions accordingly. Never store wp-config.php, .env files, or database credentials in the Git repository — keep them on the server only and exclude them with .gitignore. For team workflows, consider GitHub Actions or GitLab CI instead of bare-repo hooks — they provide deployment logs, rollback capabilities, and environment-specific configuration management.