Gitignore Configuration¶
A well-configured .gitignore file prevents sensitive data, build artifacts, and environment-specific files from entering version control. This guide covers essential patterns for Python and Django projects.
Why Gitignore Matters¶
Critical Security
A properly configured .gitignore is essential for security. Accidentally committing secrets, credentials, or API keys can lead to serious security breaches. Once committed, these secrets remain in Git history even after deletion.
Key Benefits: - Security: Prevents committing sensitive data - Clean repository: Excludes generated files and artifacts - Faster operations: Reduces repository size - Team consistency: Same files ignored across environments - CI/CD efficiency: Smaller clones, faster builds
Essential Python Patterns¶
Byte-compiled Files¶
Python creates compiled bytecode files during execution. These should never be committed:
Why Exclude: - Generated automatically by Python - Platform and version specific - Recreated on every run - Pollute version control - Cause merge conflicts
Distribution and Packaging¶
Build artifacts from packaging tools:
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
Why Exclude:
- Generated during python setup.py build
- Created by pip install -e .
- Recreated during packaging
- Large binary files
- Environment specific
Virtual Environments¶
Python virtual environment directories:
Why Exclude:
- Large (hundreds of MB)
- Environment specific
- Platform dependent
- Recreated from requirements.txt
- Different paths per developer
Best Practice: Document virtual environment setup in README instead:
# Setup
```bash
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
pip install -r requirements/base.txt
### Python Distribution Tools
```gitignore
# PyInstaller
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# PEP 582
__pypackages__/
Django-Specific Patterns¶
Static and Media Files¶
Django collects static files and stores uploaded media:
# Django static files
staticfiles/
static_root/
/static/
STATIC_ROOT/
# Media files (user uploads)
media/
media_root/
/media/
MEDIA_ROOT/
# Keep directory structure
!public/.keep
!public_collected/.keep
Why Exclude:
- staticfiles/: Generated by collectstatic
- media/: User uploads, often large files
- Served by web server in production
- Recreated or synced separately
Exception: Sometimes you want to keep the directory structure:
Database Files¶
Local development databases:
Why Exclude: - Contains potentially sensitive data - Large file size - Environment specific - Migrations recreate schema - Use fixtures for test data instead
Django Migrations¶
Controversial: Some teams commit migrations, others don't.
# Option 1: Keep migrations (recommended)
# Don't ignore migrations - they're part of schema definition
# migrations/ - DON'T ADD THIS
# Option 2: Ignore migrations (not recommended)
# */migrations/*.py
# !*/migrations/__init__.py
Our Recommendation: Commit migrations - Migrations are code - Define database schema - Required for production deployments - Track schema evolution - Enable rollbacks
Do Ignore:
Django Cache and Sessions¶
Environment and Configuration¶
Environment Variables¶
Critical: Never commit environment files with secrets:
# Environment files
.env
.env.*
.env.local
.env.production
.env.staging
.env.development
# BUT allow example file
!.env.example
Why Critical: - Contains secrets (API keys, passwords) - Database credentials - Service tokens - Once in Git history, hard to remove - Security breach risk
Best Practice: Provide .env.example:
# .env.example (committed to Git)
DEBUG=True
SECRET_KEY=your-secret-key-here
DATABASE_URL=postgresql://user:pass@localhost/dbname
AWS_ACCESS_KEY_ID=your-key-here
AWS_SECRET_ACCESS_KEY=your-secret-here
# .env (NOT committed, in .gitignore)
DEBUG=True
SECRET_KEY=actual-secret-key-xyz123
DATABASE_URL=postgresql://realuser:realpass@localhost/mydb
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Docker Configuration¶
# Docker override files
docker-compose.override.yml
.dockerignore
# Docker data volumes
**/data/
postgres-data/
redis-data/
mysql-data/
Why Exclude:
- docker-compose.override.yml: Local customizations
- Data volumes: Persistent data, large size
- Environment specific
Keep in Git:
Devcontainer Settings¶
# Personal devcontainer settings
.devcontainer/personal-settings.json
.devcontainer/docker-compose.override.yml
Why Exclude: - Personal preferences - Local path configurations - Developer-specific settings
Keep in Git:
- .devcontainer/devcontainer.json
- .devcontainer/docker-compose.yml
Testing and Coverage¶
Test Artifacts¶
# Test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
.pytest_cache/
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
pytestdebug.log
# VCR cassettes (optional - see note below)
# tests/fixtures/cassettes/
Why Exclude: - Generated during test runs - Can be large (HTML coverage reports) - Recreated on every test run - CI generates fresh reports
VCR Cassettes: Whether to commit VCR cassettes is debatable:
# Option 1: Commit cassettes (recommended for API tests)
# Ensures consistent test data
# Don't add to .gitignore
# Option 2: Ignore cassettes (if cassettes contain sensitive data)
tests/fixtures/cassettes/
*.yaml in cassettes/
Our Recommendation: Commit cassettes unless they contain sensitive data.
Coverage Configuration¶
Don't ignore coverage config files:
IDE and Editor Files¶
JetBrains IDEs¶
PyCharm, IntelliJ, WebStorm, etc.:
# JetBrains IDEs
.idea/
*.iml
*.ipr
*.iws
# CMake (from CLion)
cmake-build-*/
# Keep shared settings (optional)
!.idea/runConfigurations/
!.idea/codeStyles/
Why Exclude:
- .idea/: Contains personal preferences
- Large XML files
- Platform specific
- Each developer has different settings
Optional: Share some settings:
# Share some settings, ignore others
.idea/*
!.idea/codeStyles/
!.idea/runConfigurations/
!.idea/vcs.xml
Visual Studio Code¶
# VS Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
Why Selectively Commit: - Share useful configurations (settings.json) - Ignore personal preferences - Consistent debugging configs (launch.json)
Recommended VS Code Settings to Commit:
// .vscode/settings.json (committed)
{
"python.defaultInterpreterPath": "${workspaceFolder}/venv/bin/python",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true
},
"files.exclude": {
"**/__pycache__": true,
"**/*.pyc": true
}
}
Vim¶
# Vim
[._]*.s[a-v][a-z]
!*.svg
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
Emacs¶
# Emacs
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# Directory configuration
.dir-locals.el
# Projectile
.projectile
Operating System Files¶
macOS¶
# macOS
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
Why Exclude: - macOS system files - Automatically created - No value in version control - Cause noise in commits
Windows¶
# Windows
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
Linux¶
# Linux
*~
# Temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
Logs and Temporary Files¶
Log Files¶
Why Exclude: - Can grow very large - Change constantly - Environment specific - No historical value
Exception: Sample logs for documentation might be committed.
Temporary Files¶
Node.js and Frontend¶
Django projects often include frontend build tools:
# Node dependencies
node_modules/
npm-debug.log
yarn-error.log
yarn.lock
package-lock.json
# Frontend build artifacts
dist/
build/
.parcel-cache/
# Optional: Keep yarn.lock or package-lock.json
# For reproducible builds
!package-lock.json
!yarn.lock
Why Exclude:
- node_modules/: Very large, reinstalled from package.json
- Lock files: Debatable, keep for reproducibility
- Build artifacts: Regenerated by build process
Documentation Build¶
Why Exclude: - Generated HTML/PDF - Large files - Rebuilt for deployment - Source Markdown is committed
Service-Specific Files¶
AWS and Cloud¶
Celery¶
Mailpit (Local Email Testing)¶
Redis¶
Caddy¶
Type Checkers¶
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Pyright
.pyright/
Profiling Data¶
Complete .gitignore Template¶
Based on real-world Django projects:
# Python & Django .gitignore
# Based on production Django applications
### Environment & Secrets ###
.env
.env.*
!.env.example
.direnv
### Python ###
# Byte-compiled / optimized
__pycache__/
*.py[cod]
*$py.class
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual environments
venv/
env/
ENV/
env.bak/
venv.bak/
.venv/
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
.pytest_cache/
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
pytestdebug.log
# Type checkers
.mypy_cache/
.dmypy.json
dmypy.json
.pyre/
.pytype/
# PEP 582
__pypackages__/
### Django ###
# Database
*.db
*.sqlite
*.sqlite3
db.sqlite3
# Static and media files
staticfiles/
static_root/
/static/
media/
media_root/
/media/
# Keep directory structure
public/*
!public/.keep
public_collected/*
!public_collected/.keep
# Cache
*.cache
.cache/
# Log files
*.log
django.log
celery.log
gunicorn.log
# Celery
celerybeat-schedule
celerybeat.pid
### Docker ###
docker-compose.override.yml
**/data/
postgres-data/
redis-data/
mysql-data/
localstack/
### IDE & Editors ###
# JetBrains
.idea/
*.iml
*.ipr
*.iws
cmake-build-*/
# VS Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
.history/
# Vim
[._]*.s[a-v][a-z]
!*.svg
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
Session.vim
Sessionx.vim
.netrwhist
*~
tags
[._]*.un~
# Emacs
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
.org-id-locations
*_archive
.dir-locals.el
.projectile
### Operating Systems ###
# macOS
.DS_Store
.AppleDouble
.LSOverride
Icon
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
*.stackdump
[Dd]esktop.ini
$RECYCLE.BIN/
*.lnk
# Linux
.fuse_hidden*
.directory
.Trash-*
.nfs*
### Node.js (Frontend) ###
node_modules/
npm-debug.log
yarn-error.log
assets/node_modules/
.eslintcache
### Documentation ###
docs/_build/
doc/_build/
/site
### Temporary Files ###
*.tmp
*.temp
*.swp
*.swo
### Service-Specific ###
# LocalStack
.localstack/
# Mailpit
mailpit.db
mailpit.db-shm
mailpit.db-wal
# Redis
dump.rdb
# Caddy
caddy/data/
# Profiling
.prof
*.prof
### Personal & Local ###
.devcontainer/personal-settings.json
*.attach_pid*
.aider*
**/.claude/settings.local.json
# Random files
\#*
toggle-app.*.sh
Global Gitignore¶
Configure a global gitignore for OS and editor files:
# Create global gitignore
cat > ~/.gitignore_global << 'EOF'
# OS files
.DS_Store
Thumbs.db
# Editor files
.vscode/
.idea/
*.swp
*~
# Python
__pycache__/
*.pyc
.env
venv/
EOF
# Configure Git to use it
git config --global core.excludesfile ~/.gitignore_global
Benefits:
- Keep project .gitignore focused
- Personal preferences don't pollute project file
- Consistent across all projects
Best Practices¶
✅ Do¶
- Review before adding: Understand what you're ignoring
- Start comprehensive: Use complete template, remove what you don't need
- Keep organized: Group related patterns with comments
- Document exceptions: Explain
!negation patterns - Use wildcards carefully: Be specific to avoid over-ignoring
- Commit
.gitignoreearly: First file in new repository - Update regularly: Add patterns as project evolves
- Test patterns: Use
git check-ignore -v <file>to test
❌ Don't¶
- Don't ignore files that should be version controlled
- Don't commit secrets then add to
.gitignore - Don't use overly broad patterns (
*,**/*) - Don't ignore configuration files (commit example versions)
- Don't forget platform-specific patterns
- Don't ignore without understanding why
Pattern Syntax¶
Wildcards:
# Single character
?.txt # Matches a.txt, b.txt, not ab.txt
# Multiple characters
*.log # Matches debug.log, error.log
# Recursive
**/*.pyc # Matches in all subdirectories
Directories:
Negation:
# Ignore all .txt files except README.txt
*.txt
!README.txt
# Ignore directory contents but keep structure
logs/*
!logs/.keep
Anchoring:
# Root only
/config.py # Only root config.py, not app/config.py
# Anywhere
config.py # Matches config.py in any directory
Testing Gitignore¶
Check What's Ignored¶
# Check if file is ignored
git check-ignore -v filename.py
# Check multiple files
git check-ignore -v file1.py file2.log
# List all ignored files
git status --ignored
# Show ignored files in directory
git status --ignored --short
Debug Patterns¶
# See which .gitignore rule matches
git check-ignore -v path/to/file
# Output:
# .gitignore:12:*.log path/to/debug.log
# ^file ^line ^pattern ^matched-file
Cleaning Ignored Files¶
# See what would be removed
git clean -ndX
# Remove ignored files
git clean -fdX
# Remove ignored and untracked files
git clean -fdx
Fixing Committed Secrets¶
If you accidentally commit a secret:
Immediate Action Required
If you commit a secret, assume it's compromised. Rotate credentials immediately.
Remove from Recent Commit¶
# If not yet pushed
git rm --cached .env
git commit --amend -m "Remove accidentally committed .env file"
# Add to .gitignore
echo ".env" >> .gitignore
git add .gitignore
git commit -m "Add .env to gitignore"
# Rotate compromised credentials immediately
Remove from History¶
Use git filter-branch or BFG Repo-Cleaner:
# Using BFG (easier, faster)
# Install: brew install bfg
# Remove file from all history
bfg --delete-files .env
# Cleanup
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# Force push (coordinate with team!)
git push origin --force --all
git push origin --force --tags
# Rotate all exposed credentials
Force Push Coordination
Force pushing rewrites history. Coordinate with your team before doing this. All team members must delete their local clones and re-clone.
Version Control for Configuration¶
Configuration Strategy¶
Never Commit:
- .env files with actual secrets
- API keys, passwords, tokens
- Database credentials
- Private keys, certificates
Always Commit:
- .env.example with placeholders
- Configuration templates
- Default values (non-secret)
- Documentation of required variables
Example Structure:
# Project structure
myproject/
├── .env # Ignored, local secrets
├── .env.example # Committed, template
├── settings/
│ ├── base.py # Committed, base settings
│ ├── development.py # Committed, dev defaults
│ ├── production.py # Committed, prod structure
│ └── local.py # Ignored, local overrides
└── .gitignore
# .env.example (committed)
DEBUG=False
SECRET_KEY=change-me-to-random-string
DATABASE_URL=postgresql://user:pass@localhost/dbname
REDIS_URL=redis://localhost:6379/0
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
# .env (not committed, in .gitignore)
DEBUG=True
SECRET_KEY=django-insecure-$k!8h3n*v^2p&x@9m#q7
DATABASE_URL=postgresql://developer:devpass123@localhost/myapp_dev
REDIS_URL=redis://localhost:6379/0
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Advanced Patterns¶
Conditional Ignoring¶
# Ignore in specific directories only
/logs/*.log # Only /logs, not /app/logs
app/logs/*.log # Only app/logs directory
# Ignore everything except specific files
/config/*
!/config/base.yml
!/config/schema.json
Project-Specific Overrides¶
Pattern Debugging¶
# Create test file
touch test.pyc
# Check if ignored
git check-ignore -v test.pyc
# Output: .gitignore:2:*.pyc test.pyc
# Add to staging
git add test.pyc
# Warning: The following paths are ignored by one of your .gitignore files
Migration Guide¶
Adding .gitignore to Existing Project¶
If your project doesn't have .gitignore:
# 1. Create comprehensive .gitignore
curl https://www.toptal.com/developers/gitignore/api/python,django > .gitignore
# 2. Review and customize
vim .gitignore
# 3. Remove cached files
git rm -r --cached .
git add .
git commit -m "Add comprehensive .gitignore"
# 4. Clean up ignored files
git clean -fdX
Updating Existing .gitignore¶
# 1. Check currently tracked files
git ls-files | grep -E "\.(pyc|log|db)$"
# 2. Add new patterns to .gitignore
echo "*.log" >> .gitignore
# 3. Remove from index
git rm --cached *.log
# 4. Commit changes
git add .gitignore
git commit -m "Update gitignore to exclude log files"
Next Steps¶
- Git Workflow - Branch strategies and commit conventions
- Pre-commit Hooks - Automated checks before commits
- Secrets Management - Proper handling of sensitive data
- Environment Variables - Configuration best practices