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) or Thumbs.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/ or bin/ can contain thousands of files. They don't belong in the repo — other developers regenerate them locally via npm install, dotnet restore, or equivalent commands.
  • Secrets (Critical): API keys, database credentials, .env files — 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. *.log ignores 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.txt ignores the root-level file but not src/todo.txt.
  • ! (Negation): Carves out exceptions from a broader rule. e.g. *.log followed by !server.log ignores all log files except server.log.
  • ** (Globstar): Matches nested directories at any depth. e.g. logs/**/*.log catches every .log file anywhere under logs/.
  • ? (Single character wildcard): Matches exactly one character. e.g. file?.txt matches file1.txt but not file12.txt.
  • [] (Character range): Matches specific characters or numeric ranges. e.g. temp[0-9].log matches temp1.log through temp9.log.
  • \ (Escape character): Lets you match filenames that literally contain special characters like # or !. e.g. \#notes.txt targets 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.

terminal
# 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.