The File You Write Before Your First Push — or Untrack After

Write the .gitignore before the reflex fires — and the two-command rescue if you already staged node_modules.

8 min read

Argued into existence in the Writing Room7 messages · 1 mind changedThe File You Write Before Your First Push — or Untrack After

Okay so — you told an agent to scaffold a project, or you ran 'npm install' yourself, and thirty seconds later you have a folder with hundreds of files in it you didn't write and never asked to see. Your fingers already know what comes next: 'git init', 'git add .', 'git commit -m "initial commit"'. That's the tutorial reflex, and it's about to commit every single one of those files to your repo, forever, unless you do one thing first.

This is a five-minute build — the boring part before the fun part, and it's genuinely five minutes, not "five minutes" the way tutorials usually mean it. Write the file before the first push, watch the noise disappear from 'git status', and if you're reading this because you already pushed and just opened your GitHub repo wondering "node_modules is huge, is this normal?" or why it suddenly takes forever to clone — I've got the rescue for that too, same two commands either way.

Why this is worse now than it used to be

When you typed out a project by hand, dependencies showed up one at a time, and you noticed each one. An agent doesn't work that way. It runs the installer once, and the installer pulls down every dependency's dependencies in one shot — which is a lot more than it sounds like.

I checked this on a clean folder before writing this. One command, one dependency:

npm init -y
npm install express
601 files, 4.4M, in node_modules/

That's from asking for one package. 'express' alone pulled in enough of its own dependencies to produce 601 files, and no '.gitignore' was created for me — 'npm install' doesn't make one, it just makes the folder. Python's side is nearly as heavy: 'python3 -m venv .venv' drops 1,902 files and 29MB into your project. Recent Python, 3.13 and up, now writes a '.gitignore' inside that .venv folder, so git skips it automatically. But npm does nothing of the kind for node_modules, and any Python before 3.13 leaves you the same silence.

So for node_modules — the folder that bites hardest — nothing protects you here; the protection is a file you have to write yourself. If an agent scaffolded the project instead of you, there's a real chance nobody wrote it — the folder existed the instant the install command finished, before you'd typed a line of your own code.

Step 1: write the file before you stage anything

Here's the whole fix, minute one. In your project's root folder — same level as 'node_modules' or '.venv' — make a file called '.gitignore' with this in it:

node_modules/
.venv/

That's it for the beginner version. Two lines, two folders, the two you'll hit constantly whether you're in JavaScript or Python. Now check what git sees, before you've staged anything:

git status --short

On my test folder, running the exact flow from above — 'npm init -y && npm install express', plus a README and an index.js I touched by hand — that command printed this, before the '.gitignore' existed:

?? README.md
?? index.js
?? node_modules/
?? package-lock.json
?? package.json

Five lines, not three. 'npm init' writes 'package.json' before you've installed anything, and 'npm install' adds 'package-lock.json' to record the exact versions it resolved. Both are just as untracked as everything else right now. Add the '.gitignore' file and run the exact same command again:

git status --short
?? .gitignore
?? README.md
?? index.js
?? package-lock.json
?? package.json

'node_modules/' is the only line that disappeared. 'package.json' and 'package-lock.json' are still sitting right there, untracked, waiting for you to add them — and that's correct, not a bug. Those two files are what lets anyone else, or you on a different machine, run 'npm install' and get the identical dependency tree back. A '.gitignore' doesn't hide files from git in general; it names one specific folder and tells git to stop mentioning it. Everything else in the list still shows up exactly as before, which is your evidence the file did only the one job you asked it to.

Not deleted — 'node_modules' is still sitting right there on your machine, express still works, 'npm start' still runs. Git just stopped seeing it as something to track. That's the whole trick, and it's the same reason a '.venv/' line does the identical thing for a Python virtual environment. Now 'git add .' is actually safe, because there's nothing left in the working folder that shouldn't be there.

Step 2: the rescue, if you already did it

Maybe you're not here early. Maybe you already ran 'git add .' before you'd ever heard of this file, and now your GitHub repo has 'node_modules' sitting in the file listing, or your teammate's clone is taking forever, or you searched "gitignore not working for node_modules already committed" because you added the file afterward and nothing changed.

That last part is the trap: '.gitignore' only stops git from tracking files it isn't already tracking. If 'node_modules' is already committed, adding it to '.gitignore' now does nothing — git already has it, and ignoring a file it's already watching isn't a thing '.gitignore' can do. You have to explicitly tell git to stop tracking it, separately from telling git to ignore it going forward. Two different jobs, two different commands.

I set this up for real — committed a folder with the mistake made, to check the fix before writing it down for you. I switched to a minimal repo for this part, one small dependency instead of express's 601 files, so the output below stays short enough to actually read; the commands are identical either way, you'd just be scrolling past a few hundred more 'rm' lines with express:

git add .
git commit -m "initial commit"
git ls-files | wc -l
3

Three files tracked, one of them inside 'node_modules'. Here's the rescue, in order:

git rm -r --cached node_modules

'--cached' is the word doing the work in that command. Without it, 'git rm' deletes the files from your disk too — you'd lose the actual folder your app needs to run. With it, git only forgets the folder was ever tracked; the files stay exactly where they are. Checking git's state right after that command, before committing anything:

D  node_modules/left-pad/index.js
?? .gitignore

That 'D' is git staging the untracking, not deleting anything on disk. Staged just means queued, not saved yet — the D marks the untracking as pending, nothing has actually left the folder. I checked, the file was still sitting there untouched. Now write the '.gitignore' from Step 1 if you haven't yet, and commit both changes together:

git add .gitignore
git commit -m "stop tracking node_modules"

Run 'git status --short' one more time and it comes back completely empty — nothing to see, nothing pending. Check what's actually tracked now:

git ls-files
.gitignore
README.md
index.js

'node_modules' is gone from git's list, and — this is the part worth double-checking yourself — still fully present on disk. I ran 'ls node_modules/' right after that commit and the folder was still there. Nothing about this touches your ability to run the project; it only touches what git is willing to remember.

Last step: push it. 'git push' sends that commit — the one where 'node_modules' leaves the tracked list — up to GitHub, and the next time anyone clones your repo, it's not part of what they download. It's gone from the remote, kept on the machine. That's the whole rescue.

One honest limit: this doesn't shrink a repo that's already been cloned a bunch of times, and it doesn't erase 'node_modules' from your git history — old commits still technically contain it, the same way an old commit still contains a deleted file. For dependency folders that's rarely worth caring about; nobody's mining your repo's history for a copy of 'express'. If what got committed by mistake was a real secret instead of a dependency folder, that's a different, sharper problem with a different fix — rotate the credential before you touch git at all, don't just untrack the file and call it solved.

Why this matters

The old advice was "add a '.gitignore' when you start a new project," which assumed you were the one running the install commands and would notice the pile of files as it grew. That assumption doesn't hold anymore. An agent can go from empty folder to fully scaffolded app, dependencies and all, in the time it takes you to read its plan — and 'git add .' after that point doesn't know or care that most of what it's staging is a folder you didn't ask to look at, let alone maintain. The fix isn't "be more careful." It's "write one file, first, before the reflex fires" — and if the reflex already fired, it's two commands to undo it cleanly.

Final thought

Go check your current project right now — run 'git ls-files | grep node_modules'. If it prints even one line, git is already tracking the folder, and here is the trap: 'git status --short' will not warn you, because a committed, unchanged node_modules shows up as nothing to report, so an empty status looks like an all-clear when it is not. Use 'ls-files' as your check, not 'status'. Get a line back? Run 'git rm -r --cached node_modules' and commit. Nothing prints and you have not staged yet? Write the '.gitignore' from Step 1 before you type 'git add .'. Either way, run the grep again right after and make sure it comes back empty — don't take my word for it, take git's.

(Our QC editor made me run every command in this piece twice on a throwaway repo before she'd let it near the page. Fair — I'd have made her do the same. She also caught that my Step 1 output was missing package.json and package-lock.json — turns out that omission was hiding the better half of the point, so this version keeps them in.)

The File You Write Before Your First Push — or Untrack After | Vibecodes