Every Git repository has files that don’t belong in version control. Build output, dependency folders, API keys sitting in .env files. Commit them once by accident and you’re dealing with bloated repos, merge conflicts, or worse, leaked credentials.
So what is gitignore, and why does it matter? A .gitignore file tells Git exactly which files and directories to skip when tracking changes. It’s a simple text file, but getting it wrong (or forgetting it entirely) creates real problems that cost teams time and money.
This guide covers how .gitignore works, the pattern syntax you need to know, where to place the file, how to fix common issues with already-tracked files, and how to debug rules that aren’t doing what you expect.
What is a .gitignore File

A .gitignore file is a plain text file that tells Git which files and directories to exclude from version control. It sits inside your Git repository and acts as a filter between your working directory and the staging area.
Git reads .gitignore before you stage anything. If a file matches a pattern listed there, Git pretends it doesn’t exist. No tracking, no diffs, no accidental commits.
The file itself is just lines of text. Each line is a pattern that describes what to ignore. You can target specific files, entire directories, or broad categories using wildcards.
Here’s what makes it click. When you run git status, ignored files won’t appear in the untracked list. When you run git add, they won’t get staged. The file works quietly in the background of your entire Git workflow.
According to the Stack Overflow Developer Survey, 93.87% of developers use Git as their primary version control system (RhodeCode, 2025). Every single one of those developers deals with .gitignore files regularly.
The .gitignore file gets committed to the repository like any other file. That means everyone who clones or pulls the repo gets the same ignore rules. It’s a shared agreement about what belongs in the codebase and what doesn’t.
Why Developers Use .gitignore

Repositories without a .gitignore file get messy fast. Build artifacts, dependency folders, config files with passwords, OS-generated junk. All of it ends up tracked, bloating the repo and creating problems nobody asked for.
Keeping Sensitive Data Out of Repositories
This is the big one. And most people learn this lesson the hard way.
GitHub detected over 39 million leaked secrets across its platform in 2024 alone (GitHub Blog). API keys, database passwords, OAuth tokens, cloud credentials. All committed to repositories by developers who either forgot or didn’t have a .gitignore in place.
GitGuardian’s 2025 report found that 70% of secrets leaked in 2022 are still valid today. That means attackers can use two-year-old credentials that nobody bothered to rotate. IBM’s 2024 Cost of Data Breach Report puts the average cost of breaches involving compromised credentials at $4.88 million per incident.
A properly configured .gitignore excludes .env files, API key configs, and certificate files from the first commit. It doesn’t fix everything, but it catches the most common mistakes before they happen. Teams working on software development projects should treat this as a baseline security measure, not an afterthought.
Preventing Repository Bloat
Build artifacts and dependency folders are the usual culprits. A typical Node.js project’s nodemodules folder can hit 200MB to 1GB depending on the number of packages installed. Committing that to a repository makes cloning painfully slow for everyone on the team.
Same story with compiled output. The dist/ or build/ directory gets regenerated every time you run a build. Tracking it in Git creates unnecessary diffs and inflates repository size without adding any real value.
GitHub hosts over 420 million repositories as of 2024, and more than 90% of Fortune 100 companies use the platform in their development workflows (SQ Magazine). At that scale, even small per-repo inefficiencies compound into serious storage and performance issues.
Reducing Merge Conflicts
Machine-specific files cause the dumbest merge conflicts. Your IDE generates a .idea/workspace.xml file. Your teammate’s Visual Studio creates a .vs/ directory. Neither file belongs in the shared repo, but without .gitignore, both end up there, and both change constantly.
The .gitignore file eliminates these conflicts before they start. When everyone on a team uses the same ignore rules, the only files that get tracked are the ones people actually wrote.
.gitignore Pattern Syntax

The .gitignore syntax looks simple at first. Then you hit a case where your pattern isn’t matching and spend twenty minutes wondering why. It’s worth understanding the rules up front.
Every line in a .gitignore file represents a pattern. Blank lines are ignored. Lines starting with # are comments.
| Syntax | What It Does | Example |
|---|---|---|
* | Matches everything except / | *.log ignores all log files |
** | Matches nested directories | /build ignores any build folder at any depth |
/ (trailing) | Targets directories only | dist/ ignores the dist directory |
! | Negates a previous pattern | !important.log re-includes a specific file |
? | Matches any single character | file?.txt matches file1.txt, fileA.txt |
Pattern matching is relative to where the .gitignore file lives. A pattern like build/ in the root .gitignore targets the root-level build directory. But the same pattern in src/.gitignore targets src/build/.
Took me forever to figure out that order matters. Later rules override earlier ones. If you ignore all .txt files and then negate one specific file, the negation has to come after the broad rule. Flip the order and it breaks.
Common Pattern Examples
OS-generated files:
.DSStore(macOS creates these in every folder you open in Finder)Thumbs.db(Windows thumbnail cache)desktop.ini(Windows folder config)
Language-specific build output:
.classfor Java compiled bytecodepycache/and.pycfor Pythondist/andbuild/for JavaScript projects
Dependency folders:
nodemodules/for npm and Yarnvendor/for PHP Composer and Go modules.gradle/for Gradle-based Java projects
Environment and credentials:
.env,.env.local,.env.production.pem,.key(certificates and private keys)config/secrets.yml
Hutte research shows that 55% of developers use pre-commit hooks to catch unintended file changes or secrets before they get committed. But .gitignore remains the first line of defense.
Where to Place a .gitignore File
Most developers drop a single .gitignore in the repository root and call it done. That works for many projects. But Git actually supports multiple .gitignore files at different levels, and where you place them changes what they affect.
Root-Level .gitignore
This is the standard. A .gitignore file at the root of your repository applies its rules to every file and directory in the project.
Practically all shared ignore rules belong here. Build output, dependency directories, environment files, IDE configs that the whole team agrees to exclude. It gets committed to the repo, so every collaborator picks up the same rules when they clone it or pull from GitHub.
Subdirectory .gitignore Files
Git lets you place .gitignore files in any subdirectory. The rules inside apply only within that directory’s scope.
This can be useful in monorepo setups where different parts of the project use different languages or build tools. A frontend/.gitignore might ignore nodemodules/ while a backend/.gitignore targets pycache/. The rules don’t bleed into each other.
When multiple .gitignore files exist, the closest one wins. Git checks the .gitignore in the current directory first, then works upward toward the root. A pattern in a child directory overrides a conflicting pattern in the parent.
Global .gitignore for User-Wide Rules
Some files have nothing to do with the project. They’re specific to your machine, your editor, your operating system.
.DSStore, Thumbs.db, .idea/, .vscode/. These shouldn’t clutter the project’s .gitignore because not everyone on the team uses the same setup.
Git handles this with git config:
git config --global core.excludesFile ~/.gitignoreglobal
This applies your personal ignore rules across every repository on your machine. The file never gets committed anywhere, so it stays completely private.
.gitignore Templates for Popular Languages and Frameworks

Writing .gitignore rules from scratch is a waste of time for most projects. Templates exist for basically every language and framework you’d work with, and they’ve been refined by thousands of contributors.
GitHub’s Official gitignore Repository
GitHub maintains a public repository at github/gitignore with over 150 templates organized by programming language, framework, and tooling. The repo has more than 150,000 stars and 80,000 forks (DEV Community, 2024).
When you create a new repository on GitHub, the platform pulls from this collection to offer you a .gitignore template in the setup wizard. Just select your language and the file gets added automatically.
Templates cover Python, Node.js, Java, Unity, Go, Rust, Swift, Kotlin, and dozens more. Each one defines patterns for build output, dependency caches, and environment files specific to that stack.
gitignore.io by Toptal
Toptal’s gitignore.io tool lets you combine multiple templates at once. Working on a project that uses Python, VSCode, and macOS? Select all three and the tool merges them into one file.
The tool supports 571 operating system, IDE, and programming language templates (Toptal). It generates more detailed output than GitHub’s defaults, including patterns for less common tools and framework-specific edge cases.
| Source | Templates Available | Best For |
|---|---|---|
| GitHub gitignore repo | 150+ | Clean, minimal templates by language |
| Toptal gitignore.io | 571 | Combining multiple stacks into one file |
| IDE built-in generators | Varies | Quick setup during project init |
When to Customize a Template
Templates are a starting point. Your mileage may vary depending on project setup.
If your team uses a build pipeline with custom output directories, you’ll need to add those paths manually. Same if you’re working with Docker and want to exclude container-specific files that templates don’t cover by default.
The general rule: start with a template, then add project-specific patterns after your first few commits when you notice files showing up in git status that shouldn’t be there.
How to Create a .gitignore File

Creating the file itself takes about five seconds. Getting the timing right matters more than most people think.
Creating Before the First Commit
This is the ideal scenario. Before you run git commit for the first time, the .gitignore should already be in place.
From the terminal:
touch .gitignore
Then open it in any text editor and add your patterns. If you’re starting a Node.js project, nodemodules/ and .env should be the first two lines. For Python, pycache/ and .pyc. For Java, .class and target/.
The reason timing matters: once a file is tracked by Git, adding it to .gitignore later doesn’t untrack it. Git remembers. So setting up .gitignore first avoids a whole class of problems.
Creating Through an IDE or Platform
GitHub: Offers a .gitignore template picker when you create a new repository through the web interface.
VSCode: You can create the file directly in the file explorer. Some extensions generate templates automatically based on detected languages.
JetBrains IDEs: IntelliJ, WebStorm, and PyCharm all have built-in .gitignore support with syntax highlighting and pattern completion.
Verifying the File Works
After creating your .gitignore, run git status. If ignored files still appear as untracked, either the pattern is wrong or the file was already tracked before you added the rule.
For targeted debugging, use:
git check-ignore -v filename
This tells you exactly which .gitignore file and which line is responsible for ignoring (or not ignoring) a specific file. Saves a lot of guessing.
Teams that follow a structured software development process typically include .gitignore setup as part of their project initialization checklist, right alongside setting up the continuous integration pipeline and configuring source control management.
How to Ignore Files Already Tracked by Git

This trips up nearly everyone at some point. You add a pattern to .gitignore, run git status, and the file still shows up. Nothing seems broken. The pattern looks right. But Git keeps tracking it anyway.
The reason is straightforward: .gitignore only applies to untracked files. If a file was committed before the ignore rule existed, Git continues monitoring it regardless of what your .gitignore says.
Using git rm –cached
The fix is git rm with the --cached flag. This removes the file from Git’s index (the staging area) while keeping it on your local disk.
git rm --cached filename.ext
For an entire directory, add the -r flag:
git rm -r --cached nodemodules/
After running this, you still need to commit the change. The next push to GitHub will remove the file from the remote repository too. Your local copy stays untouched.
The Nuclear Option: Clearing the Entire Cache
Sometimes you have dozens of files that need untracking. Going one by one gets tedious fast.
The broader approach clears the entire Git index and re-adds everything, letting .gitignore filter out what it should:
git rm -r --cached .git add .git commit -m "Reset tracking to respect .gitignore"
Be careful with this one. On large repositories, it rewrites the index completely. Make sure your .gitignore is correct before running it, or you might untrack files you actually need.
The Risk of Previously Committed Secrets
Untracking a file does not erase it from commit history. If you committed an API key or a database password before adding the .gitignore rule, that secret is still sitting in older commits.
GitGuardian’s 2025 report found that 96% of leaked GitHub tokens had write access, meaning a single exposed credential from an old commit can give attackers significant control over a project’s infrastructure.
Removing sensitive data from Git history requires tools like git filter-branch or BFG Repo-Cleaner. Both rewrite commit history, which affects every collaborator who has cloned the repo. It’s a much bigger operation than just updating .gitignore. Teams that follow solid software development best practices set up .gitignore before the first commit to avoid this problem entirely.
Global .gitignore vs. Repository .gitignore

Two levels. Different purposes. Mixing them up creates confusion for your entire team.
| Feature | Repository .gitignore | Global .gitignore |
|---|---|---|
| Scope | Single project | All repos on your machine |
| Committed to repo | Yes, shared with team | No, private to you |
| Best for | Build output, dependencies, .env | OS files, editor configs |
| Setup | Create .gitignore in project root | git config --global core.excludesFile |
When to Use a Global .gitignore
Editor and IDE files are the classic use case. Your .idea/ directory from JetBrains, your .vscode/ settings, Vim swap files. These are personal to your development environment and have nothing to do with the project itself.
Operating system files fit here too. macOS generates .DSStore in every directory. Windows creates Thumbs.db. Neither belongs in a shared repository, but adding them to every project’s .gitignore is redundant.
Set it once and forget it:
git config --global core.excludesFile ~/.gitignoreglobal
Why Team Rules Belong in the Repo
Anything the whole team needs to ignore goes in the project-level .gitignore. Period.
If you put nodemodules/ in your global config instead of the repo’s .gitignore, a new teammate who clones the project won’t have that rule. They’ll accidentally commit the entire dependency folder on their first push.
The Atlassian Git tutorial documentation puts it clearly: patterns meant to be shared across clones should live in .gitignore, while patterns specific to one user’s workflow belong in the global file or in .git/info/exclude.
Good configuration management keeps project-wide rules visible to everyone. Personal preferences stay personal.
.gitignore vs. .git/info/exclude
Most developers have never opened this file. It exists in every Git repository by default, sitting quietly inside the .git directory.
How .git/info/exclude Works
The .git/info/exclude file uses the exact same pattern syntax as .gitignore. Anything you can write in one works in the other.
The key difference: it never gets committed. The .git/ directory is local to your machine and excluded from pushes. So any rule you add to .git/info/exclude is invisible to everyone else on the team.
When to Use Each One
.gitignore: shared project rules (build output, dependencies, environment files). Gets committed, travels with the repo.
.git/info/exclude: personal, project-specific rules you don’t want teammates to see. Maybe a local todo.md file, a personal test script, or temporary debug output that only matters to you.
Global .gitignore: machine-wide rules that apply across all repos (editor configs, OS-generated files).
The official Git documentation spells it out: patterns that all developers want to ignore go in .gitignore, patterns specific to one user’s workflow go in $GITDIR/info/exclude, and patterns for all situations go in the global config.
Portability Difference
If you delete and re-clone a repository, the .gitignore file comes back with it. Your .git/info/exclude rules are gone.
This makes .gitignore the right default for anything that affects how the project builds or runs. Reserve .git/info/exclude for throwaway personal rules you don’t mind losing. Teams managing complex projects with multiple contributors and deployment pipelines typically standardize everything in the committed .gitignore and leave .git/info/exclude for individual developer preferences.
Debugging .gitignore Rules That Are Not Working

Your .gitignore looks correct. The pattern matches. But git status still shows the file. This is one of the most common frustrations in Git, and there are only a handful of actual causes.
The File Was Already Tracked
This is cause number one. Probably 90% of “my .gitignore isn’t working” complaints come down to this single issue.
The fix: git rm --cached <file>, then commit. We covered this in detail earlier, but it’s worth repeating because it catches so many people off guard.
Using git check-ignore for Targeted Debugging
git check-ignore -v <filename>
This command tells you exactly which .gitignore file and which line number is responsible for ignoring (or failing to ignore) a specific file. If it returns nothing, Git isn’t applying any ignore rule to that file.
Pair it with git diff to check if the file has unstaged changes that might be causing confusion during debugging.
Encoding and Whitespace Problems
This one is tricky because you can’t see it. Windows PowerShell (version 5) sometimes saves files in UTF-16 encoding. Git expects UTF-8.
A .gitignore file saved as UTF-16 looks perfectly normal in your editor but every single rule silently fails. GitHub’s troubleshooting threads are full of developers who spent hours debugging patterns before discovering the encoding was wrong.
The fix: re-save the file as UTF-8. Then clear the cache and re-add.
Rule Order and Pattern Conflicts
Later rules override earlier ones. If you write *.log on line 1 and !important.log on line 5, the exception works. Flip the order and it breaks.
| Problem | Cause | Fix |
|---|---|---|
| File still tracked after adding rule | Already committed before rule existed | git rm --cached |
| Pattern has no effect | File encoding is UTF-16 | Re-save as UTF-8 |
| Negation rule not working | Parent directory already excluded | Restructure pattern order |
| Case mismatch | Git is case-sensitive on Linux | Match exact filename casing |
Git is case-sensitive by default. On Linux, Config.json and config.json are two different files. If your .gitignore targets one and your actual file uses different casing, the rule won’t match.
When something isn’t working, run git check-ignore -v first. It cuts through the guessing and tells you exactly what’s happening. Tools that support linting for configuration files can also catch syntax issues before they become problems in your source control setup.
FAQ on What Is Gitignore
What is a .gitignore file used for?
A .gitignore file tells Git which files and directories to exclude from tracking. It prevents build artifacts, dependency folders like nodemodules, and sensitive files like .env from being committed to your repository.
Where should I put my .gitignore file?
Place it in the root directory of your Git repository. Rules apply to the entire project from there. You can also add .gitignore files in subdirectories for scoped rules, though most projects only need one.
Why is .gitignore not ignoring my file?
The file was likely tracked before you added the rule. Run git rm --cached filename to untrack it, then commit. The .gitignore only prevents untracked files from being staged.
How do I create a .gitignore file?
Run touch .gitignore in your terminal, or create it through your IDE. Add one pattern per line. Do this before your first commit to avoid tracking files you’ll need to untrack later.
What is the difference between .gitignore and .git/info/exclude?
Both use the same syntax. The .gitignore file gets committed and shared with your team. The .git/info/exclude file stays local to your machine and is never pushed to the remote repository.
Can I have multiple .gitignore files in one repository?
Yes. Git supports .gitignore files in any subdirectory. Rules in a child directory override conflicting rules from parent directories. The closest .gitignore to a file takes priority.
What is a global .gitignore file?
A global .gitignore applies ignore rules across every repository on your machine. Set it up with git config --global core.excludesFile. Best used for OS files like .DSStore and editor configs.
How do I ignore a folder in Git?
Add the folder name with a trailing slash to your .gitignore file. Writing nodemodules/ ignores that entire directory. The slash tells Git to target directories specifically, not files with the same name.
Does .gitignore remove already committed files?
No. Adding a rule to .gitignore does not retroactively untrack files. You need to run git rm --cached to remove them from the index first, then commit the change for it to take effect.
Where can I find .gitignore templates?
GitHub maintains an official collection at github/gitignore with over 150 templates for popular languages and frameworks. Toptal’s gitignore.io tool lets you combine multiple templates into one file.
Conclusion
Understanding what is gitignore comes down to one thing: controlling what enters your repository and what stays out. A properly configured .gitignore file protects credentials, keeps your commit history clean, and eliminates pointless merge conflicts across your team.
Set it up before your first commit. Use templates from GitHub’s official collection or Toptal’s gitignore.io to cover your programming language and framework. Customize from there based on your project’s specific build output and tooling.
If files are already tracked, git rm –cached fixes it. If rules aren't working, git check-ignore -v` tells you why.
The .gitignore file is small. The problems it prevents are not. Get it right early, and your distributed version control workflow stays smooth from the first push to production.
- Tailwind CSS Cheat Sheet - June 9, 2026
- The Stuff Nobody Tells You About Hiring Web Design Services - June 9, 2026
- How to Create a Pull Request in GitHub Easily - June 8, 2026



