datarekha

Ignoring files with .gitignore

Keep secrets, build artifacts, and node_modules out of your repository using .gitignore patterns and git rm --cached.

5 min read Beginner Git Lesson 6 of 15

What you'll learn

  • What .gitignore is and where it lives
  • How glob patterns, negation, and anchoring work in .gitignore
  • The crucial difference between untracked files and already-committed files

Before you start

Why a clean repository matters

When someone clones your repository they should get source code — not your Python bytecode cache, not a 200 MB node_modules folder, and definitely not your API keys. A repository bloated with generated or secret files is slower to clone, harder to review, and potentially dangerous to share.

Git’s answer is .gitignore: a plain-text file that tells Git which files and folders to pretend do not exist.


Where .gitignore lives

Place .gitignore in the root of your repository. Git reads it automatically — no configuration needed.

my-project/
├── .gitignore        ← the main one
├── src/
│   └── app.py
└── data/
    └── .gitignore    ← optional: rules for this subdirectory only

You can also place a .gitignore inside any subdirectory. Rules in a nested .gitignore apply only within that subdirectory. For most projects a single root-level file is all you need.


Pattern syntax

Each line in .gitignore is a glob pattern — a wildcard expression similar to what your shell uses for filename matching.

PatternWhat it ignores
*.logAny file ending in .log, anywhere in the tree
build/The entire build directory (trailing / means directory)
# commentNothing — lines starting with # are comments
!keep.logRe-includes keep.log even if a prior rule excluded it (negation)
/secret.txtOnly secret.txt at the repo root (leading / anchors to root)
**/logsA directory named logs anywhere in the tree (**/ means recursive)
doc/**/*.pdfAll .pdf files inside doc/ and any of its subdirectories

Glob means the pattern language where * matches any sequence of characters (except /), ? matches one character, and [abc] matches a set. It is not regular-expression syntax.


A realistic .gitignore

Here is a file you could drop into a Python project that also has a JavaScript build step:

# === Python ===
__pycache__/
*.py[cod]
*.pyo
.venv/
venv/
dist/
*.egg-info/

# === Secrets ===
.env
.env.*
!.env.example

# === Node / frontend ===
node_modules/
dist/
.next/
.nuxt/

# === OS files ===
.DS_Store
Thumbs.db

# === Editors ===
.vscode/
.idea/
*.swp

A few notes on the patterns above:

  • *.py[cod] uses a character class to match .pyc, .pyo, and .pyd in one rule.
  • .env.* covers .env.local, .env.production, etc. The !.env.example negation re-includes the example file so teammates can copy it as a starting point.
  • dist/ appears under both Python and Node because both ecosystems write output there. Duplicate rules are harmless.

The crucial gotcha: tracked vs. untracked

The practical order of operations is:

  1. Create .gitignore with your patterns before your first git add.
  2. If you already committed a file by accident, run git rm --cached and commit the removal.
  3. Rotate any leaked credentials regardless of step 2.

The filter in action

Working Directoryapp.py.envnode_modules/pycache/.gitignoredropped (.env, node_modules/, pycache/)What Git seesapp.py only
The .gitignore filter sits between your working directory and Git’s index. Files that match a pattern never reach git status or git add.

Ready-made templates

You rarely need to write a .gitignore from scratch. Two authoritative sources:

  • github/gitignore — the official collection at github.com/github/gitignore. Each file is named after a language or framework (Python.gitignore, Node.gitignore, etc.).
  • gitignore.io (also at toptal.com/developers/gitignore) — generates a combined file for multiple environments at once. Search “Python macOS VSCode” and download the result.

Copy the relevant template into your repo root, trim anything that does not apply, and add your own project-specific entries at the bottom.


Quick check

0/3
Q1You add node_modules/ to .gitignore, but git status still shows node_modules/ as modified. What is the most likely cause?
Q2Your .gitignore contains the line *.log and later the line !important.log. What happens to important.log?
Q3A colleague accidentally committed .env with real AWS keys six months ago, then immediately ran git rm --cached .env and committed the removal. Are the keys still accessible?

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Explore further

Cheat sheets

Related lessons

Skip to content