A Python .gitignore ignores bytecode (__pycache__/, *.py[cod]), packaging output (build/, dist/, *.egg-info/), virtual environments such as .venv/, the .env file, and tool caches like .pytest_cache/. Those are the key lines of the tool’s Python template, which has 49.
What the blocks ignore
- Bytecode and extensions:
__pycache__/,*.py[cod]and*.so, all rebuilt from source. - Packaging:
build/,dist/,wheels/,*.egg-info/,*.egg, and/lib/,/lib64/and/var/, anchored to the root, where a virtualenv or buildout made in the project leaves them. Asrc/lib/of yours stays. - Environments:
.venv/,venv/,env/andENV/, plus.env, the file python-dotenv reads secrets from. - Tools:
.pytest_cache/,.mypy_cache/,.ruff_cache/,.tox/,.nox/, coverage’s.coverageandhtmlcov/, Jupyter’s.ipynb_checkpoints/, and PyInstaller’s*.specand*.manifest.
In a scratch repository with this file, git check-ignore -v -n --no-index names the line that matched each path, or prints :: when none did:
.gitignore:3:__pycache__/ app/__pycache__/main.cpython-313.pyc
.gitignore:34:.venv/ .venv/bin/python
.gitignore:30:.env .env
:: .env.example
.gitignore:10:dist/ dist/app-1.0-py3-none-any.whl
.gitignore:22:*.egg-info/ src/app.egg-info/PKG-INFO
:: poetry.lock
:: uv.lock
:: requirements.txt
.gitignore:15:/lib/ lib/python3.13/site-packages/six.py
:: frontend/src/lib/api.ts
.gitignore:40:*.spec app.spec
Commit the lockfile and pyproject.toml
uv.lock, poetry.lock, pdm.lock and a pinned requirements.txt are what make every machine install the same versions; uv’s documentation says its lockfile belongs in version control. Commit .env.example too, with the variable names and no values.
The *.spec trap
PyInstaller writes app.spec on its first run, which is why the template ignores *.spec. Many projects then edit that file by hand, and with the line in place those edits never reach the repository. Re-include yours with a line after the template; it works because the file’s folder isn’t excluded:
.gitignore:63:!myapp.spec myapp.spec
.gitignore:40:*.spec other.spec
A ! line can’t reach inside an excluded directory: after .venv/, Git never looks in the folder, so no file in it can come back.
Bytecode was committed before the file?
.gitignore only affects untracked files, and __pycache__/ folders are scattered through the tree. Untrack everything, add back what the file allows, and commit; the working tree isn’t touched:
git rm -r --cached .
git add .
git commit -m "Apply .gitignore"
Editor and OS files
PyCharm’s .idea/, .vscode/ and .DS_Store come from your machine. Pick the JetBrains or VS Code template, or keep them in a global ignore file:
git config --global core.excludesFile ~/.gitignore_global