Git Ignore Generator

Generate a clean, editable .gitignore file for your project by combining common templates. Pick one or more stacks, review the generated rules, add your own patterns, then copy or download the final file.

Ignore dependencies, build output, logs, and local env files.


How to Use the Git Ignore Generator

1

Select your project type

Select your project type.

2

Choose additional patterns

Choose additional patterns.

3

Copy the generated .gitignore file

Copy the generated .gitignore file.

.gitignore Generator — Create the Right .gitignore for Your Project Stack

Every Git repository needs a .gitignore. Without one, git add . stages everything — your node_modules/ folder (potentially hundreds of megabytes), your .env file with database passwords and API keys, IDE-specific folders, compiled bytecode, log files, and OS metadata. These files either shouldn't be in version control, can be regenerated from source, or are specific to one developer's machine and would create constant merge conflicts.

A well-crafted .gitignore keeps your repository small, your commit diffs meaningful (actual code changes instead of regenerated file noise), and — most critically — keeps secrets off GitHub, GitLab, and Bitbucket where they're visible to anyone with repository access. This generator combines templates for the most common languages, frameworks, and tools, merges them into a single clean file, and lets you copy or download the result.

What Belongs in Every Project's .gitignore

Regardless of language or framework, certain categories of files should almost always be excluded:

Environment and secrets: .env, .env.local, .env.production, .env.*.local. These contain database connection strings, API keys, JWT secrets, and third-party service credentials. They must never be committed. Commit a .env.example with placeholder values as documentation for required variables instead.

Dependency directories: node_modules/ (Node.js), vendor/ (Composer/PHP), .venv/ and __pycache__/ (Python), .bundle/ (Ruby), target/ (Java/Rust). These are fully reproducible from lock files by running the package manager install command. Committing them adds enormous bloat — a typical Node project's node_modules/ can exceed 200MB — and makes every diff noisy with auto-generated dependency files.

Build output: dist/, build/, out/, .next/, .nuxt/, compiled *.class or *.o files. These are generated from source and should be reproducible by running the build command. Committing build output means your repository history fills with binary artifacts that obscure the actual code changes.

IDE and editor metadata: .idea/ (JetBrains), .vscode/ (VS Code — though shared settings may be committed), *.suo and *.user (Visual Studio), .DS_Store (macOS), Thumbs.db (Windows). These are developer-specific and constantly appear as unstaged changes for everyone else on the team.

Logs and temporary files: *.log, logs/, tmp/, temp/. Log files change on every request and carry no useful version history.

Pattern Syntax — How .gitignore Actually Works

Understanding the pattern syntax prevents the most common mistakes. A .gitignore file is processed line by line, and each non-empty, non-comment line defines a single pattern. Comments start with # and blank lines are ignored — they exist purely for readability in what can otherwise become a very dense file.

No slash = match anywhere: *.log ignores all .log files in every subdirectory. Trailing slash = directory only: dist/ ignores the directory but not a file named dist. Leading slash = anchored: /config.local.php matches only at the .gitignore's level, not in subdirectories. This distinction matters more than people realize — a common mistake is writing /build/ when you actually mean build/ for project-wide exclusion.

* matches any characters within a single directory level. ** matches across directory boundaries: **/logs/ finds logs/ anywhere in the tree. ? matches a single character. [abc] matches any one of the listed characters. You can combine these: *.min.js catches minified files, **/logs/**/*.log catches every log file inside any logs directory, and build/[0-9]*.txt catches only files in the build directory starting with a digit.

A leading ! negates a pattern, un-ignoring files that would otherwise be matched. But there's a critical caveat: you cannot un-ignore a file inside a directory that's itself ignored — the parent directory ignore takes precedence and the negation silently fails. This is the number one source of "my .gitignore isn't working" confusion. For example, writing vendor/ followed by !vendor/important-config.php will not work. You need to un-ignore the specific file before ignoring the parent, or use a more targeted pattern like vendor/** with !vendor/important-config.php placed before it.

Cross-platform gotcha: Patterns are case-sensitive on Linux/macOS but case-insensitive on Windows. *.PDF will match *.pdf on Windows but not on a Linux CI server. Be explicit about casing in mixed-OS teams. If you develop on macOS and deploy to a Linux runner, a casing mismatch can cause files to be tracked on one system and ignored on another.

Negation Patterns — The Subtle Power and Pitfalls

Negation patterns (the ! prefix) are one of the most misunderstood features in .gitignore. They let you make exceptions to broader ignore rules, which sounds straightforward. In practice, the precedence rules trip up even experienced developers.

The key rule to internalize is this: a negation pattern only works if Git has not yet decided to ignore the parent directory. Once an entire directory is ignored, its contents are invisible to Git regardless of any negation patterns. Think of it like a building with the doors locked — you can put up signs saying "this room is open," but nobody can see them because they can't get in.

A practical example: suppose you want to ignore everything in build/ except build/config.json. Writing build/ followed by !build/config.json does not work. Instead, write build/* (which ignores files inside build without making the directory itself invisible) and then !build/config.json. The difference between build/ and build/* is subtle but crucial — the trailing slash makes the directory ignored entirely, while /* only matches contents at one level.

Another common pattern is ignoring all environment files except the example template. You would write .env* on one line, then !.env.example on the next. This works because .env* is a file-level pattern, not a directory-level pattern, so the negation has a chance to take effect. Many team leads enforce this pattern in their .gitignore templates to prevent accidental secret leakage.

IDE-Specific and OS-Specific Ignore Patterns

Every major IDE and operating system generates metadata files that have no business in a Git repository. The trouble is that these files are different for each tool, and including them all in the project .gitignore clutters it with rules that are irrelevant to developers who use different tools. The solution is a two-tier approach.

JetBrains IDEs (IntelliJ, WebStorm, PhpStorm, PyCharm) create .idea/ directories containing workspace settings, run configurations, and local history. Some of these files, like code style configurations, a team might want to share. You can commit specific files within .idea/ (like codeStyles/ or inspectionProfiles/) while ignoring the rest using negation patterns:

.idea/
!.idea/codeStyles/
!.idea/codeStyles/**
!.idea/inspectionProfiles/
!.idea/inspectionProfiles/**

Visual Studio creates *.suo, *.user, *.userosscache, and .vs/ directories. VS Code creates .vscode/ — though some teams commit shared launch and task configurations from within it. Vim users create *.swp, *.swo, and .*.un~ swap files. Emacs creates *~ backup files and \#*\# autosave files.

On macOS, .DS_Store files appear in every directory you open in Finder. On Windows, Thumbs.db and desktop.ini appear in directories with custom icons. Linux rarely has equivalent issues, but some desktop environments create .directory files.

Rather than adding all of these to the project .gitignore, configure a global .gitignore file for your personal machine. This keeps the project file focused on project concerns and avoids debates with teammates about whether to include .idea/ or .vscode/ rules.

Global .gitignore — Setup and Configuration

A global .gitignore applies ignore rules across all repositories on your machine without touching any individual repo's configuration. It is the correct place for your personal IDE and OS files.

To set one up, create a file at a location of your choice (commonly ~/.gitignore_global on Unix or C:\Users\YourName\.gitignore_global on Windows) and tell Git to use it:

git config --global core.excludesFile ~/.gitignore_global

A solid starting point for a global .gitignore covers the most common culprits across platforms and editors:

# OS metadata
.DS_Store
Thumbs.db
desktop.ini
*.swp
*.swo
*~

# IDE
.idea/
.vscode/
*.suo
*.user
*.code-workspace

# Build from common tools
*.pyc
__pycache__/
node_modules/
vendor/

One mistake to avoid: do not put project-specific patterns in your global .gitignore. If every developer on a Python project needs to ignore __pycache__/, that rule belongs in the project's .gitignore, not in each person's global file. Global rules are for things unique to your personal setup.

Build Artifact Patterns Across Languages

Build artifacts are files generated by compilers, bundlers, or other toolchains from your source code. They are the classic "regenerable" files that should never be version-controlled. The specific patterns vary by ecosystem, and getting them wrong leads to bloated repositories or, worse, inconsistent builds when developers accidentally commit different versions of compiled output.

JavaScript/Node.js: dist/, build/, .cache/, .parcel-cache/, .webpack/. Webpack's default output is dist/, Vite defaults to dist/, Parcel creates .parcel-cache/ and dist/. If you use Babel, you might have a lib/ or esm/ directory. Check your bundler's documentation for the default output directory.

Python: *.pyc, __pycache__/, *.egg-info/, dist/, build/, .eggs/. If you use virtual environments, add .venv/, venv/, or env/. Python's __pycache__ directories are notoriously easy to miss because they appear inside your source tree, not at the root.

Java: target/ (Maven), build/ (Gradle), *.class, *.jar, *.war. Gradle users should also ignore .gradle/ and Maven users should ignore .mvn/ unless they've committed the Maven wrapper.

Rust: target/ is the build output directory and can be enormous — a single build can produce hundreds of megabytes of intermediate files. Always ignore it.

Go: The bin/ directory is the conventional output location for compiled binaries. Some projects also ignore vendor directories if they use Go modules, though this is a matter of team preference.

Handling Environment Files and Secrets

Environment files are the most dangerous category to get wrong. A leaked .env file in a public GitHub repository has led to countless data breaches, cryptocurrency mining incidents, and unauthorized infrastructure access. The pattern for handling them has a few layers.

First, ignore all environment files broadly: .env, .env.*, *.env. Then negate the example template: !.env.example, !.env.example.*. The .env.example file is committed as documentation — it contains the variable names with placeholder or empty values, telling other developers exactly which environment variables the application requires.

Be aware that some frameworks create multiple environment-specific files. Laravel generates .env, .env.backup, and .env.testing. Next.js uses .env.local, .env.development, .env.production. All of these should be ignored. Your .gitignore should use broad enough patterns to catch them without listing each variant explicitly.

For CI/CD pipelines, secrets are injected as environment variables through the platform's secrets management (GitHub Actions secrets, GitLab CI variables, etc.) rather than committed to the repository. This separation of secrets from source code is a fundamental security practice, and your .gitignore is the last line of defense preventing accidental commits.

Global vs. Repository-Level .gitignore

Repository-level .gitignore: Committed to the repo and shared with all contributors. This is for project-specific exclusions — node_modules/, vendor/, .env, build directories, and language-specific generated files. Every developer cloning the repo gets these rules automatically.

Global .gitignore: Your personal ignore file, configured with git config --global core.excludesfile ~/.gitignore_global. This is the place for IDE-specific files (.idea/, .vscode/) and OS metadata (.DS_Store, Thumbs.db) — things that are irrelevant to the project itself and shouldn't pollute the shared .gitignore with developer-tool-specific rules.

Per-directory .gitignore: You can place a .gitignore inside any subdirectory. Its patterns are relative to that directory. This is useful for ignoring build output within a specific package in a monorepo, or ignoring test fixtures in a particular test directory.

Monorepo .gitignore Strategies

Monorepos — repositories containing multiple projects or packages — require a more thoughtful approach to .gitignore configuration. A single root-level .gitignore can work for small monorepos, but as the number of packages grows, you will want per-package .gitignore files to keep rules scoped and maintainable.

In a monorepo with a packages/ directory containing multiple microservices, each package gets its own .gitignore inside its directory. Patterns in that file are relative to the package, not the repository root. So a packages/api/.gitignore containing dist/ ignores packages/api/dist/, not dist/ at the root.

The root .gitignore handles repository-wide concerns: node_modules/ at the root, CI/CD artifacts, shared environment files, and OS metadata. This two-level approach prevents patterns from accidentally matching unrelated paths across packages.

Common Pitfalls and Debugging

The file is already tracked: .gitignore only prevents untracked files from staging. If a file was committed before you added it to .gitignore, Git keeps tracking it. Fix: git rm --cached filename, then commit the removal.

Negation inside ignored directories: Adding !vendor/important-config.php after vendor/ won't work — the parent directory ignore takes precedence. You need to ignore specific files before ignoring the parent directory, or use a more specific pattern.

Debugging with git check-ignore: Run git check-ignore -v filename to see exactly which rule matches (or doesn't match) a specific file. This is the single most useful debugging command for .gitignore issues. The -v flag is critical — without it, you only get a yes/no answer, not the specific rule that matched.

Patterns apply to untracked files only: .gitignore has no effect on files that are already in the repository's history. If you need to retroactively remove files, you must use git rm --cached or, for files deep in history, tools like git filter-repo or BFG Repo-Cleaner.

Frequently Asked Questions About .gitignore

No. .gitignore only prevents untracked files from being staged. If a file is already committed, .gitignore has no effect — Git keeps tracking it. To stop tracking: git rm --cached filename for a single file, or git rm -r --cached . to untrack everything and re-stage according to the updated .gitignore. Commit the removal. The file stays on disk but is no longer versioned.
Yes, always. The .gitignore is project configuration — every contributor and CI/CD environment needs it. Personal ignores (your IDE files, your OS metadata) go in your global ~/.gitignore_global instead. The split is simple: if a teammate would also benefit from the ignore rule, it goes in the repo's .gitignore. If it's purely about your local setup, it goes in your global file.
Anchor the pattern with a leading slash: /storage/logs/ in a root .gitignore matches only the storage/logs/ directory, not any other logs/ folder. Alternatively, place a separate .gitignore file inside that subdirectory — its patterns are relative to that directory. In monorepos, per-package .gitignore files are the standard approach.
The file is probably already tracked (see FAQ one). Other causes: wrong pattern syntax (test with git check-ignore -v filename), a negation pattern overriding the ignore, or case-sensitivity issues across operating systems. The git check-ignore -v command is your best friend here — it shows exactly which rule matches (or doesn't) and on which line of your .gitignore.
Treat every secret as compromised immediately — rotate API keys, change database passwords, revoke tokens. Don't wait to "check if the repo was public." Then: add .env to .gitignore, run git rm --cached .env, commit that change. To purge from history entirely, use git filter-repo or BFG Repo-Cleaner, then force-push. If the repo was ever public or cloned by others, assume the secrets were already extracted.
Both ignore files, but .git/info/exclude is local-only — it's never committed and never shared with anyone. It's useful for personal ignores when you don't want to set up a global gitignore file. The repository's .gitignore is committed and shared. If you find yourself adding the same exclude pattern repeatedly, it probably belongs in the committed .gitignore rather than in your personal exclude file.
Yes. Combine a directory pattern with a file pattern: **/test/*.log ignores .log files inside any test/ directory but not elsewhere. Or place a .gitignore file inside the specific directory with patterns relative to that location. For ignoring all .env files except a template: .env* followed by !.env.example — but remember the negation caveat with parent directories.
Create a file like ~/.gitignore_global and run git config --global core.excludesFile ~/.gitignore_global. Populate it with IDE files (.idea/, .vscode/), OS metadata (.DS_Store, Thumbs.db), and editor swap files (*.swp, *~). This file applies to every repository on your machine, so keep it focused on personal-tool patterns, not project-specific ones.
Yes. Run git rm --cached filename to remove the file from Git's tracking index while leaving the file untouched on your local filesystem. Then add the pattern to .gitignore to prevent it from being re-staged. This is the standard workflow for removing accidentally committed files like .env, IDE configurations, or large binaries that were added before the ignore rules were in place.