<aside> 🔀
Module 14 — Ignoring, Attributes, and Big Files (.gitignore, git lfs, submodules). Modules so far taught what Git does with your files; this one controls what enters the repository and how. .gitignore keeps clutter and secrets out; .gitattributes fixes line-ending chaos and controls diffs; Git LFS solves Module 3's binary-bloat warning; submodules embed one repo inside another. These are the tools that keep a real repository clean, portable across operating systems, and a sane size.
🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)
</aside>
<aside> 🚦
Before you start. You need Module 2 (tracked vs untracked, git rm --cached), Module 3 (the binary-bloat warning this module resolves), and Module 8 (remotes — LFS and submodules involve them). Part C needs git-lfs installed (git lfs version); Part D uses local repos as submodules. The exercises build fresh repos as needed.
</aside>
<aside> 📖
Official docs: gitignore manual · Ignoring files — GitHub Docs
</aside>
.gitignore is a file listing patterns for paths Git should not track — build output, logs, dependency directories, local secrets, editor droppings. Once a path matches, Git stops offering it in git status and won't stage it with git add .. The pattern rules: a bare name or glob (*.log, secret.key) matches anywhere; a trailing slash (build/) matches directories; a leading slash (/config.local) anchors to the repo root; and a leading ! re-includes something a previous pattern excluded (*.log then !keep.log). .gitignore files are themselves committed and can sit in any directory (applying to that subtree), so the ignore rules travel with the repo — everyone on the team ignores the same clutter automatically.
🧪 Exercise 14.1 — ignore clutter, watch status go quiet
mkdir -p ~/git-course && cd ~/git-course
git init -q kitchen && cd kitchen
echo "app code" > app.py && git add app.py && git commit -q -m "Add app"
mkdir -p logs build
echo "run log" > logs/app.log
echo "artifact" > build/out.o
echo "secret=123" > .env
git status -s # clutter everywhere
cat > .gitignore <<'EOF'
# ignore logs and build output
*.log
build/
.env
EOF
git status -s # ...silenced
<aside> 📖
Official docs: gitignore manual
</aside>
Here is the trap that catches everyone once: .gitignore only affects untracked files. If a file is already tracked (you committed it before ignoring it), adding it to .gitignore does nothing — Git keeps tracking it, keeps showing its changes, keeps committing them. The fix is Module 2's git rm --cached <file>: stop tracking it (remove from the index) while keeping it on disk, then commit that removal. From that commit forward, the .gitignore rule takes over. And git check-ignore -v <path> is the debugger for "why is (or isn't) this ignored?" — it names the exact .gitignore file and line that decides.
🧪 Exercise 14.2 — the trap, and the escape
cd ~/git-course/kitchen
git add .gitignore && git commit -q -m "Add gitignore"
echo "config data" > config.cache
git add config.cache && git commit -q -m "Add config.cache" # oops, committed it
echo "*.cache" >> .gitignore # now try to ignore it...
git status -s # ...gitignore did NOT untrack it
git rm --cached config.cache # the real fix
git status -s
git commit -q -am "Stop tracking config.cache"
git check-ignore -v config.cache # now Git confirms it's ignored
ls config.cache # still on disk
<aside> ⚠️
Trap: the deepest cut of the already-tracked trap is secrets. Adding .env to .gitignore after it was committed does not remove it from history — the secret sits in every past commit, fetchable by anyone with the repo (Module 3: commits are immutable, deleting the current file changes nothing about old snapshots). git rm --cached stops future tracking but the leaked value is already in the object database forever. Real remediation is two things: rotate the secret (assume it's compromised — it left your machine), and if it must be scrubbed from history, rewrite the entire history with git filter-repo (Module 15) and force-push — an all-hands, disruptive operation. The only cheap fix is prevention: ignore secrets before the first commit, and never commit them in the first place.
</aside>
<aside> 🗑️
Real-world analogy — the "do not file" note on a desk
.gitignore is a sticky note on your inbox: "don't file anything matching these — junk mail, drafts, sticky notes themselves." New matching papers get skipped automatically. It keeps your filing cabinet (the repo) full of only real documents.
Where the analogy stops working. The note governs incoming papers only — it has zero power over documents already filed. Slap "don't file bank statements" on your inbox and the twelve statements already in the cabinet stay filed until you physically pull them (git rm --cached). And pulling today's copy doesn't erase the photocopies in last year's archive boxes (old commits) — which, for a leaked secret, is the whole problem. The note prevents; it does not un-file, and it certainly does not shred the archive.
</aside>