A clean Git repo is small, secret-free, and contains only the files that matter. A messy one is bloated with screenshots, videos, dependency folders, and (worse) credentials. The line between the two is a single text file at the root of your project: .gitignore. This lesson covers the patterns that keep test automation repos clean, the recovery move for the file you accidentally committed last week, and the rule that has saved more careers than any other Git skill: never commit secrets.
What .gitignore does
.gitignore is a plain-text file you create at the root of your repository. Each line is a pattern. Any file or folder matching a pattern is invisible to Git — it doesn't show up in git status, can't be staged with git add, can't be committed.
Create it once:
cd your-test-repo
touch .gitignore # macOS/Linux/Git Bash
# or on Windows PowerShell:
# New-Item .gitignoreOpen it in your editor and start adding patterns.
Why test projects need it badly
Run a Cypress or Playwright suite once and look at what your test directory contains:
node_modules/— hundreds of MB of dependencies, restored on demand bynpm install.cypress/screenshots/— hundreds of PNGs from failed runs.cypress/videos/— multi-megabyte recordings of every test.playwright-report/— generated HTML reports.test-results/— raw artefacts from each run..env— contains the staging API token, the seeded test password, the bot account credentials.
Without .gitignore, all of that gets pulled into your first commit. Your "small test repo" balloons to a gigabyte; your secrets land in a public PR; your teammates' clones take 20 minutes. The fix is half a minute of .gitignore work before the first commit.
A starter .gitignore for a Cypress/Playwright project
Drop this into the root of any test automation repo and tweak from there:
# Dependencies
node_modules/
# Cypress artefacts
cypress/screenshots/
cypress/videos/
cypress/downloads/
# Playwright artefacts
test-results/
playwright-report/
playwright/.cache/
# Test reports
reports/
mochawesome-report/
allure-results/
coverage/
# Environment and secrets
.env
.env.local
.env.*.local
cypress.env.json
# IDE
.vscode/settings.json
.idea/
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
Commit this on day one. Future you and every teammate will thank you.
Pattern syntax — what each symbol means
.gitignore patterns are not full regex; they're a small dedicated mini-language:
| Pattern | Matches | Example |
|---|---|---|
node_modules/ | A folder anywhere in the repo | node_modules/, cypress/node_modules/ |
*.log | Any file ending in .log | npm-debug.log, cypress.log |
**/*.tmp | Any .tmp file at any depth | tmp/foo.tmp, a/b/c/foo.tmp |
/dist | Only dist at the root | dist/ (root only, not tests/dist/) |
!keep.log | Negate — include this file even if other patterns ignore it | with *.log and !keep.log, every .log is ignored except keep.log |
# comment | A comment | (purely human-readable) |
A real combined example — ignore all of cypress/screenshots/ but keep one reference image used by visual tests:
cypress/screenshots/
!cypress/screenshots/baseline-login.png
Verifying your .gitignore is working
After adding patterns, check what Git now sees:
git statusIf node_modules/ no longer appears as untracked, you're set. If it still appears, your pattern is wrong (or the file was already tracked — read on).
To debug a single file:
git check-ignore -v cypress/screenshots/login.png.gitignore:5:cypress/screenshots/ cypress/screenshots/login.png
Git tells you exactly which line of which file matched. If nothing prints, the file isn't ignored — your pattern hasn't matched.
The "I already committed it" recovery
.gitignore only affects files that haven't been tracked yet. If you committed node_modules/ last week and then added node_modules/ to .gitignore, the folder is still tracked. Adding the pattern does nothing retroactively.
The fix: untrack the file (or folder) in Git while keeping it on disk:
git rm --cached -r node_modules/
git commit -m "Stop tracking node_modules; rely on .gitignore"--cached removes from Git's index but leaves the actual file alone. -r recurses into folders. Push the commit; teammates' next pull will delete the folder from their copies (locally on their disks) — they'll need to npm install again to restore it. Communicate the change before pushing.
Never commit secrets
The single most expensive mistake in .gitignore discipline. Once a secret hits Git history — even briefly, even on a private repo — it should be considered compromised:
- API tokens, OAuth secrets, AWS keys.
- Database connection strings with passwords.
- Any
.envfile containing real values. - TLS private keys, signing keys, JWT secrets.
- Test account passwords if they grant any meaningful access.
If you spot a leak, the order is non-negotiable:
- Rotate / revoke the secret first. It's in history; assume it's been seen.
- Add the pattern to
.gitignore. git rm --cached <file>and commit.- For genuinely sensitive cases, rewrite history with
git filter-repoor BFG and force-push. Coordinate with the team.
The companion convention: commit a .env.example (no real values) so new joiners know what variables exist:
# .env.example — copy to .env and fill in real values
CYPRESS_BASE_URL=https://staging.example.com
CYPRESS_TEST_USERNAME=
CYPRESS_TEST_PASSWORD=
.env.example is tracked. .env is in .gitignore. Everyone wins.
GitHub's template .gitignore files
You don't need to write one from scratch. GitHub maintains github.com/github/gitignore — official templates for Node.js, Python, Java, and dozens of other ecosystems. When you create a new repo on GitHub, the "Add .gitignore" dropdown lets you pick one. Pick Node for a Cypress/Playwright project, then add the test-specific lines from the starter above.
The Git for QA cheat sheet has the most-used QA .gitignore patterns as a quick reference.
What to ignore — at a glance
- – node_modules/
- – .pnpm-store/
- – vendor/
- – cypress/screenshots/
- – cypress/videos/
- – playwright-report/
- – test-results/
- – allure-results/
- – .env
- – cypress.env.json
- – *.pem
- – credentials.json
- .DS_Store –
- Thumbs.db –
- .vscode/settings.json –
- .idea/ –
- *.log –
- npm-debug.log* –
- .cache/ –
⚠️ Common mistakes
- Adding
.gitignoreafter the first commit. A file already tracked stays tracked, no matter what.gitignoresays. Always create.gitignorebefore the firstgit add .— and if you missed it, usegit rm --cached -r <path>to untrack and commit the cleanup. - Committing
.envwith real credentials. The "I'll just remove it later" mistake — by the time you remove it, anyone watching the repo has a copy. Treat.envas radioactive: keep it ignored from day one and commit a.env.exampletemplate instead. - Ignoring
node_modules/but then including it in a Docker image somehow. A common follow-on confusion: the file is gitignored (correct) but somehow ends up in CI artefacts (wrong)..gitignoreonly affects Git; CI tools, Docker, and bundlers each have their own ignore files (.dockerignore,.npmignore). They aren't synced; you may need to mirror patterns.
🎯 Practice task
Set up a clean .gitignore on a real test repo. 20-25 minutes.
- In your
qa-sandboxrepo (or a real test repo if you have one), open the root and create.gitignore. Paste the starter list above. - Commit and push:
git add .gitignore && git commit -m "Add .gitignore for test artefacts and secrets" && git push. - Create a fake
.envfile:echo "CYPRESS_TEST_PASSWORD=hunter2" > .env. Rungit status. Confirm.envdoes NOT appear. - Run
git check-ignore -v .envand read which line of.gitignorematched. - Create
cypress/screenshots/test.png(any placeholder). Rungit status. Confirm it's not listed. - Negation: add
cypress/screenshots/baseline-login.png(placeholder). Add!cypress/screenshots/baseline-login.pngto.gitignore. Rungit statusagain — confirm only the baseline appears. - Recovery drill: intentionally commit a file (e.g.,
notes.txt). Then addnotes.txtto.gitignore. Rungit rm --cached notes.txt && git commit -m "Stop tracking notes.txt". Confirmgit statusno longer shows the file. - Stretch: start a brand new project with
npm init -y && npm install cypress. Runls. Add a.gitignoreso thatgit statusshows only the source files you'd actually want to commit.
The next lesson covers the test data that should live in Git — fixtures — and how to organise them so they help rather than hurt.