Chapter 1.5☕ 12 min read

.gitignore Mastery — Keep Junk Out of Git

.env commit kar diya? Beta, key rotate karo aur git rm --cached seekho.

01Why .gitignore — Keep Junk Out of Git

Not every file in your project belongs in Git. Some are generated, some are secrets, some are OS junk.

If you commit node_modules/, your repo becomes 200MB+ of code written by others. Clone takes forever.

If you commit .env, your API keys are visible to anyone with repo access. Career-limiting move.

If you commit build/ or dist/, every merge has binary conflicts that cannot be resolved.

.gitignore tells Git: "These files and folders — do not track them, do not show them in status, do not include them in commits."

The .gitignore file SHOULD be committed — it is part of the project, shared with the whole team.

# Without .gitignore — chaos
git status
# Untracked files:
#   node_modules/     ← 50,000 files of dependencies
#   .env              ← SECRET=abc123  (DANGER!)
#   dist/             ← generated build output
#   .DS_Store         ← macOS junk
#   debug.log         ← temporary file
#   app.js            ← YOUR ACTUAL CODE

# With .gitignore — clean
git status
# Untracked files:
#   app.js            ← only your actual code
#   .gitignore        ← the ignore rules themselves
The most dangerous .gitignore mistake: NOT ignoring .env files. If you push API keys, database passwords, or JWT secrets to GitHub, bots will find them within MINUTES. These bots scan every public push 24/7. Once a secret is in Git history, it is there forever — even if you delete the file. You MUST rotate the key immediately.
02Pattern Syntax — Glob Patterns Explained

.gitignore uses glob patterns — simple but powerful matching rules.

*.log — ignore all .log files anywhere in the repo.

node_modules/ — ignore the entire node_modules directory (trailing / matters!).

build/ or dist/ — ignore build output directories.

.env — ignore the .env file (secrets!).

!.important.lognegation: do NOT ignore this file even though *.log is ignored.

# comment — lines starting with # are comments.

temp/ — ignore any directory named temp.

doc/*.txt — ignore .txt files directly inside doc/ (not subdirectories).

doc/**/*.txt — ignore .txt files anywhere inside doc/ (including subdirectories).

# Universal .gitignore template
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
vendor/
.pnp/
.pnp.js

# Build output
dist/
build/
out/
.next/
.nuxt/

# Environment secrets
.env
.env.local
.env.*.local

# OS generated files
.DS_Store        # macOS
Thumbs.db        # Windows
desktop.ini      # Windows

# IDE / Editor
.vscode/         # VS Code (debatable)
.idea/           # IntelliJ
*.swp            # Vim swap files

# Logs
*.log
npm-debug.log*

# Testing
coverage/

# But keep this specific file
!.env.example
EOF
03The Trap — Already Tracked Files & git rm --cached

THE most common .gitignore mistake: adding a file to .gitignore AFTER Git is already tracking it.

.gitignore only affects UNTRACKED files. If Git is already tracking a file, .gitignore is ignored for that file.

This is incredibly confusing for beginners: "I added .env to .gitignore but git status still shows it!"

Fix: git rm --cached <file> — remove from Git's tracking index WITHOUT deleting the file from your disk.

--cached means "only remove from the index (staging), keep the working directory file."

After git rm --cached, commit the removal, and THEN .gitignore takes effect.

# THE TRAP: file already tracked
echo "SECRET=abc123" > .env
git add .env
git commit -m "oops: committed .env with secrets"

# Now add to .gitignore
echo ".env" >> .gitignore
git status
# .env is STILL showing as modified! .gitignore didn't work!

# WHY? Because Git is already tracking .env
# .gitignore only applies to UNTRACKED files

# THE FIX: untrack without deleting
git rm --cached .env
# removed from index (tracking), but file still on disk

git status
# deleted:   .env  (from Git's perspective)
# Untracked: .env  (now .gitignore takes effect!)

git add .gitignore
git commit -m "fix: untrack .env and add to gitignore"

# Verify
git status  # .env no longer appears!

# CRITICAL: rotate the secret key!
# The old commit still has SECRET=abc123
# Anyone with history access can see it
# Go to your provider and generate a NEW key
💡 If you accidentally commit a secret: (1) Rotate the key IMMEDIATELY — invalidate it on the provider side. (2) git rm --cached to untrack. (3) Add to .gitignore and commit. (4) If it is a public repo, the old commit still has the secret — use git filter-repo to remove from history, or contact GitHub support.
04Global vs Repo-level vs Local .gitignore

Repo-level .gitignore: committed with the project, shared by all team members. Most common.

Global .gitignore: applies to ALL repos on your machine. For personal OS/editor junk.

Set global: git config --global core.excludesfile ~/.gitignore_global

What goes in global: .DS_Store, Thumbs.db, .idea/, *.swp — personal editor/OS files.

What goes in repo-level: node_modules/, dist/, .env — project-specific patterns.

What goes in local-only (.git/info/exclude): personal patterns you do not want to commit to the repo.

Priority: local exclude > repo .gitignore > global .gitignore.

# Set up global gitignore (do this ONCE per machine)
git config --global core.excludesfile ~/.gitignore_global

cat > ~/.gitignore_global << 'EOF'
# OS files — never commit these
.DS_Store
Thumbs.db
desktop.ini

# Editor files — personal preference
.idea/
*.swp
*.swo
*~
EOF

# Repo-level .gitignore (committed to the repo)
cat > .gitignore << 'EOF'
# Project-specific
node_modules/
dist/
.env
*.log
coverage/
EOF

# Local-only exclude (NOT committed, personal)
cat > .git/info/exclude << 'EOF'
# My personal patterns that teammates don't need
my-debug-notes.txt
experiments/
EOF

# Verify which file is ignoring a pattern
git check-ignore -v .DS_Store
# /Users/sai/.gitignore_global:2:.DS_Store    .DS_Store
# Shows: global gitignore, line 2 is the rule
05Best Practices & Common Mistakes

Do not write .gitignore from scratch — use templates from github.com/github/gitignore.

Node.js project: ignore node_modules, dist, coverage, .env, logs.

Python project: ignore __pycache__, venv, .pyc, dist, .env.

Common mistake: ignoring package-lock.json or yarn.lock — DO NOT! These ensure consistent installs.

Common mistake: ignoring .env.example — this SHOULD be committed (it is the template without real secrets).

Common mistake: ignoring .gitignore itself — it MUST be committed so the team shares the rules.

Use git check-ignore <file> to test if a file would be ignored.

# Node.js .gitignore (production-ready)
cat > .gitignore << 'EOF'
# Dependencies
node_modules/

# Build
dist/
build/

# Environment
.env
.env.local
.env.*.local

# Logs
*.log
npm-debug.log*

# Testing
coverage/

# OS
.DS_Store

# DO NOT ignore these (common mistakes):
# package-lock.json  ← needed for reproducible installs
# .env.example       ← template, safe to commit
# .gitignore         ← must be committed!
EOF

# Verify your .gitignore
git check-ignore -v node_modules/pkg.js
# .gitignore:2:node_modules/    node_modules/pkg.js ✓

git check-ignore -v app.js
# (no output = NOT ignored, will be tracked) ✓

# Find ignored files
git status --ignored
# Shows both tracked and ignored files

# GitHub's template collection
# https://github.com/github/gitignore
# Copy the template for your language/framework

Lo kar liya — Key Points:

  • ✅ .gitignore tells Git which files to NEVER track — secrets, dependencies, build output, OS junk
  • ✅ Pattern syntax: *.log (all logs), node_modules/ (directory), !.env.example (negation — do not ignore)
  • ✅ THE TRAP: .gitignore only affects UNTRACKED files — if Git is already tracking a file, adding it to .gitignore does NOTHING
  • ✅ Fix: git rm --cached <file> removes from Git tracking without deleting the file from disk
  • ✅ Global .gitignore (core.excludesfile) is for personal OS/editor files that apply to all your repos
  • ✅ .gitignore itself MUST be committed — it is a shared team configuration
  • ✅ Never ignore package-lock.json (needed for reproducible installs) or .env.example (safe template)