When you run git init and then git add ., Git stages everything it finds — images that never change, editor temp files, OS-generated junk, compiled output. Unless you tell it otherwise, all of it goes in. That's rarely what you want.
A .gitignore file is simply a list of rules that tells Git: "don't track these."
Any file or folder you list in .gitignore gets excluded from version control. It's a hidden file — which makes sense, since it's not something you'll be editing often.
What Should a .gitignore File Contain?
There are four categories worth thinking about:
- OS Files: Files like
.DS_Store(macOS) orThumbs.db(Windows) have no place in a shared repository. They cause unnecessary merge conflicts and add noise to every diff. - IDE-Specific Files: JetBrains IDEs generate
.idea/, Visual Studio creates.vs/, VS Code uses.vscode/. These are personal to your setup — committing them forces your preferences on everyone else. - Language & Platform Artifacts: Folders like
node_modules/orbin/can contain thousands of files. They don't belong in the repo — other developers regenerate them locally vianpm install,dotnet restore, or equivalent commands. - Secrets (Critical): API keys, database credentials,
.envfiles — these must never be committed. Even in a private repository, the risk is real. Once a secret is in Git history, it's extremely hard to fully remove.
The specific files you ignore will vary by language, framework, OS, and editor. But the syntax for writing the rules is always the same:
- # (Comment): Lines starting with
#are ignored by Git. Use them to organize your rules into sections. e.g.# OS Files - * (Wildcard): Matches zero or more characters. Great for targeting all files of a certain type. e.g.
*.logignores every file ending in.log. - Trailing / (Directory): Targets a folder and everything inside it. Distinguishes directories from files with the same name. e.g.
node_modules/ - Leading / (Root anchor): Restricts a rule to the root directory only — subdirectories with the same name are unaffected. e.g.
/todo.txtignores the root-level file but notsrc/todo.txt. - ! (Negation): Carves out exceptions from a broader rule. e.g.
*.logfollowed by!server.logignores all log files exceptserver.log. - ** (Globstar): Matches nested directories at any depth. e.g.
logs/**/*.logcatches every.logfile anywhere underlogs/. - ? (Single character wildcard): Matches exactly one character. e.g.
file?.txtmatchesfile1.txtbut notfile12.txt. - [] (Character range): Matches specific characters or numeric ranges. e.g.
temp[0-9].logmatchestemp1.logthroughtemp9.log. - \ (Escape character): Lets you match filenames that literally contain special characters like
#or!. e.g.\#notes.txttargets a file actually named#notes.txt.
Example .gitignore File
What you ignore depends on your stack. A .NET developer's list looks different from a Node.js developer's. The .idea/ folder only matters if you use JetBrains. Start with what's relevant to your setup and expand from there.
# OS
.DS_Store
Thumbs.db
# IDE
.idea/
.vscode/
.vs/
# .NET
bin/
obj/
*.user
# Environment variables
.env
*.env.local
Tip: gitignore.io generates a solid starting list based on your language, IDE, and OS. Worth bookmarking.
How to Create a .gitignore File
The easiest way is from the terminal, at the root of your project:
touch .gitignore
On Windows, if touch isn't available: echo. > .gitignore
You can also create it from your IDE by adding a new file — just make sure the name is exactly .gitignore, dot included.
The bottom line
A well-crafted .gitignore keeps your repository lean, your history clean, and your secrets off the internet. Make it one of the first files you create in any new project — before your first git commit.
Happy coding.