On a WordPress project Git keeps the history of code, not of the whole site. Content lives in the database, media in uploads, and both stay out of the repository. Getting this boundary right is the first step to a project you can deploy and roll back.

What goes into Git

In GitNot in Git
Theme code, own mu-plugins, configurationWordPress core (web/wp)
composer.json and composer.lockvendor/ and plugins installed by Composer
Deploy workflows and scripts.env, keys, database dumps
.env.exampleUploads

Plugins are not copied into the repository. Composer installs them from their own repositories by version, so the site repository stays small and every environment gets the same code.

Branches and environments

On 4wp.dev branches map directly to environments:

flowchart LR
    DEV["Local<br/>working branch"] -->|merge| ST["staging branch"]
    ST -->|push: automatic deploy| STG["Staging server"]
    ST -->|merge| MAIN["main branch"]
    MAIN -->|manual run| PROD["Production"]
  • staging: every push deploys to staging automatically.
  • main: production is deployed by a manual run, and only from main.

The choice of branching model is a separate decision: compare Gitflow, GitHub Flow and trunk-based development.

Pre-commit hook: checks before the commit

Our hook lives in the repository (.githooks/pre-commit) and is switched on by composer hooks, which runs automatically after composer install. It runs the same script as CI, but only on staged files:

  • Forbidden files: .env, keys, auth.json, SQL dumps.
  • Secrets in code: private keys, GitHub tokens, cloud and API keys.
  • PHP syntax: php -l on every changed PHP file.
  • Composer: composer validate.

A local hook can be skipped. That is why GitHub Actions runs the same script again before every deploy.

Rules we work by

  • Secrets, passwords and keys never go into Git or chat. The checks block them.
  • Do not mix merging a pull request on GitHub with a local merge without git pull first.
  • Activate plugins and test the site only after a successful deploy.

FAQ

Should WordPress core be in the repository?
No. Composer installs it by version, like any other dependency.

How do we get the database into Git?
We do not. The database and uploads are synced between environments separately, with backups before each production deploy.

Is a pre-commit hook enough?
No. It is the fast first check; CI repeats it where it cannot be skipped.

Related