The editor is wired into production

Starting point

~/projects on the VPS is open directly in the editor via Remote-SSH — and that directory is the production environment. A change there takes effect immediately; the commit only documents it afterwards. A second working copy exists in parallel on the MacBook Air, where the same files are inert.

Decision

I don’t treat the two copies as equivalent. Only one of them is live, and that has to be clear at all times while working — not through discipline alone, but through Git as evidence of which branch currently holds which state.

Implementation

The December 2025 finding shows why that matters: configuration files were moved to infra/nginx/conf.d instead of copied, and the production mount kept pointing at the old path. Nobody noticed for seven months — until an nginx restart took both domains offline. Today’s .gitignore deliberately excludes what doesn’t belong in the repository: .env, keys, but also data/ (496 MB of runtime data) and *.sql (dumps carrying password hashes from mysql.user). Before the first push, git ls-files checks for secrets, then git ls-tree -r origin/main checks the actual state of the remote.

Takeaway

Git would have made the December 2025 divergence visible — but it was only active on one of the two branches. The CI/CD pipeline removes the discipline of never forgetting git pull before every change: From git push to live describes how.