Skip to content

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:

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

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:

# Virtual environments
venv/
env/
ENV/
env.bak/
venv.bak/
.venv/
.env/

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:

# Ignore contents but keep directory
public/*
!public/.keep

Database Files

Local development databases:

# SQLite databases
*.db
*.sqlite
*.sqlite3
db.sqlite3

# Database dumps
*.sql
*.dump

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:

# Migration artifacts
*.pyc in migrations/
migrations/__pycache__/

Django Cache and Sessions

# Cache files
*.cache
.cache/

# Session files
django_session/

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:

# These SHOULD be committed:
# Dockerfile
# docker-compose.yml
# .dockerignore

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:

# COMMIT these files:
.coveragerc
pytest.ini
tox.ini
setup.cfg
pyproject.toml

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

# Log files
*.log
*.log.*
logs/
*.tmp

# Django logs
django.log
celery.log
gunicorn.log

Why Exclude: - Can grow very large - Change constantly - Environment specific - No historical value

Exception: Sample logs for documentation might be committed.

Temporary Files

# Temporary files
*.tmp
*.temp
*.swp
*.swo
*~
\#*
.#*

# Random 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

# Sphinx documentation
docs/_build/
doc/_build/

# MkDocs documentation
/site

Why Exclude: - Generated HTML/PDF - Large files - Rebuilt for deployment - Source Markdown is committed

Service-Specific Files

AWS and Cloud

# AWS
.aws/
*.pem
*.key

# LocalStack
localstack/
.localstack/

Celery

# Celery
celerybeat-schedule
celerybeat.pid

Mailpit (Local Email Testing)

# Mailpit
mailpit.db
mailpit.db-shm
mailpit.db-wal

Redis

# Redis
dump.rdb
redis-data/

Caddy

# Caddy
caddy/data/
Caddyfile.local

Type Checkers

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Pyright
.pyright/

Profiling Data

# Profiling
.prof
*.prof
profile_stats.txt

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 .gitignore early: 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:

# Ignore directory
build/          # Trailing slash = directory only
build           # Matches file or directory

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

# Generally ignore build artifacts
build/

# But keep documentation build artifacts
!docs/build/

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