Git workflow
Repositories, commits, branches, and pull requests
Make a new repository
Create the repository in the organisation before you write code, so the app lives there from day one.
GitHub website
- Open https://github.com/superbonfire-lab and click New repository.
- Set the owner to
superbonfire-lab. Name the repository with the rules below. - Set the visibility to Private.
- Optional: add a README and a
.gitignoretemplate for your language. - Click Create repository, then clone it:
git clone https://github.com/superbonfire-lab/client-portal.git
cd client-portalGitHub CLI
gh repo create superbonfire-lab/client-portal --private --clone
cd client-portalNaming rules
- Use kebab-case:
client-portal,invoice-api,staff-dashboard. - Do not use spaces, capitals, underscores, or dots.
- Use the same name for the repository, the Coolify resource, and the subdomain. Example:
client-portal→client-portal.cp.superbonfire.tech.
Move a zip app into Git
If an app exists only as zip files, move it into Git once. After that, do not use zips to deploy.
unzip latest-version.zip -d my-app
cd my-app
git init
git branch -M main
# Ignore secrets and build output BEFORE the first commit
cat > .gitignore << 'GITIGNORE'
node_modules/
.env
.env.*
!.env.example
dist/
build/
.next/
__pycache__/
*.pyc
.venv/
.DS_Store
GITIGNORE
git add .
git commit -m "chore: initial import from zip v1.0"
gh repo create superbonfire-lab/my-app --private --source=. --pushPowerShell has no unzip or heredoc. Use these two commands instead, and run the other steps as shown:
Expand-Archive -Path latest-version.zip -DestinationPath my-app
@"
node_modules/
.env
.env.*
!.env.example
dist/
build/
.next/
__pycache__/
*.pyc
.venv/
.DS_Store
"@ | Set-Content -Encoding utf8 .gitignoreCheck that no secret was committed:
git ls-files | grep -iE '\.env|secret|key|token|password' || echo "clean"Commit messages
Use Conventional Commits: type: short description. Write in lowercase and in the imperative ("add", not "added"). Do not end with a full stop.
| Type | Use for | Example |
|---|---|---|
feat | A new feature | feat: add invoice export to CSV |
fix | A bug fix | fix: prevent login loop on expired token |
docs | Documentation only | docs: update deployment steps |
refactor | No behaviour change | refactor: split billing service |
test | Tests | test: cover empty cart case |
chore | Maintenance, dependencies, config | chore: upgrade express to 4.19 |
ci | Pipeline or deploy config | ci: add lint step |
Add a scope when it helps: feat(auth): add password reset.
For a breaking change, add ! after the type (feat!: drop v1 API) and explain it in the commit body.
Branches and pull requests
Nobody commits straight to main. Every change goes through a branch and a pull request.
Branch names use the commit type and a kebab-case description: feat/add-invoice-export, fix/login-loop.
git checkout main
git pull origin main
git checkout -b feat/add-invoice-export
# make changes
git add -A
git commit -m "feat: add invoice export to CSV"
git push -u origin feat/add-invoice-export
gh pr create --fill --base mainA reviewer approves on GitHub. Merge with Squash and merge, then delete the branch.
- Keep each pull request small: one feature or one fix.
- Write the pull request title as a Conventional Commit. Squash merge uses it as the commit on
main. - Get at least one review before you merge.
- AI agents follow the same rules. The agent creates the branch, the commits, and the pull request. A person reviews and merges.
Day-to-day flow
1. git checkout -b feat/x one branch per feature
2. commit feat:, fix:, docs:, ...
3. push and open a pull request gh pr create --fill
4. review, then Squash and merge someone else approves
5. Coolify redeploys automatic with Auto Deploy
6. check the logs in Coolify confirm the app is healthy