The first question people ask about putting WordPress in Git is “do we commit core?”. But there is a rule that comes before that question: did we write this file? That single sentence decides what to commit, what to ignore, and the handling of secrets in the next part.
Only what you wrote is source
A WordPress directory mixes together things of completely different character. There is the theme, plugins and mu-plugins you wrote; there is core and third-party code you downloaded; there is whatever was generated at runtime — uploads, caches, logs, the upgrade scratch folder; and there is whatever differs per environment, namely wp-config.php.
Notice that wp-config.php, uploads and database dumps sit together at the bottom, and they sit together for the same reason: credentials and generated content are not source. Credentials belong to an environment; uploads and dumps are the output of running the thing. Commit either and the repository stops being a history of code and becomes a copy of data — which then spreads through clones, forks and CI logs, well outside your control.
Build output is a legitimate exception. If you have decided not to install Node on the production server, committing compiled CSS and JS is the right trade. Having made that decision, though, CI has to verify that the output still matches the source — that is part seven.
Two shapes of .gitignore
If core lives in the repository too, you only need an ignore list.
# .gitignore at the repository root
/wp-config.php
/wp-content/uploads/
/wp-content/upgrade/
/wp-content/cache/
/wp-content/debug.log
*.sql
*.sql.gz
node_modules/
.env
If instead the repository holds only your own code and core and third-party plugins are managed on the server, it is safer to deny everything and re-open exactly what you want. Installing a new plugin then cannot quietly pollute the repository.
/*
!/wp-content/
/wp-content/*
!/wp-content/themes/
!/wp-content/plugins/
!/wp-content/mu-plugins/
/wp-content/themes/*
!/wp-content/themes/our-theme/
/wp-content/plugins/*
!/wp-content/plugins/our-plugin/
One rule governs this pattern: you cannot re-include a file whose parent directory is excluded. That is why each level is opened, closed again, and then the specific child re-opened. When you cannot work out why a file is or is not ignored, do not guess — ask Git.
git check-ignore -v wp-content/themes/our-theme/style.css
It answers with the file and line number of the rule that matched.
If it is already committed
.gitignore has no effect on files already tracked. Adding a rule later does not stop them travelling. To untrack while keeping the working copy, remove them from the index only.
git rm --cached wp-config.php
git rm -r --cached wp-content/uploads
But this does not erase the past. The file is still in earlier commits, and if it held a password then that password is already leaked. Tools exist to rewrite history, yet no rewrite reaches the clones and CI logs that already exist. The only real fix is to replace the value — which is exactly the next part.
Repository discipline and the wider deploy flow continue in the development workflow archive, and if you would first like to know what state a site is in, the site diagnostic in our free tools is a reasonable starting point.
Next part
With wp-config.php out of the repository, its values need somewhere to live. The next part covers per-environment configuration and secrets — and the one thing that genuinely undoes a committed secret.