Gitignore Generator
Generate a correct .gitignore from real github/gitignore templates. Pick languages, frameworks, editors, and OS, merge duplicate patterns automatically, then copy or download the file.
Starter presets
Matches template names and the patterns inside them.
Selected (4)
Languages & Runtimes
Frameworks
Editors & IDEs
Operating Systems
Build & Tooling
Generated .gitignore
Templates
4
Rules
84
Lines
174
Duplicates merged
4
# .gitignore generated by toolsflare.com/gitignore-generator # Templates: Node, Next.js, Visual Studio Code, macOS ### Node ### # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Dependency directories node_modules/ jspm_packages/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache .cache .parcel-cache # Build output dist/ build/ out/ # Next.js build output .next/ # Nuxt.js build / generate output .nuxt .output # Gatsby files .cache/ public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ### Next.js ### # Next.js build output # Production build # Vercel .vercel # Next.js env files .env*.local # TypeScript next-env.d.ts # Turbopack .turbo/ ### Visual Studio Code ### .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json !.vscode/*.code-snippets # Local History for Visual Studio Code .history/ # Built Visual Studio Code Extensions *.vsix ### macOS ### # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk
How .gitignore pattern syntax actually works
A .gitignore file is a list of glob patterns, one per line, evaluated against paths relative to the directory containing the file. Git only consults these patterns for untracked files — that distinction is the source of almost every "my .gitignore isn't working" report.
The matching engine is fnmatch with FNM_PATHNAME: a plain * matches any run of characters except a slash, so *.log matches app.log at any depth but src/*.log matches only one level below src/.
- Leading slash anchors:
/buildmatches onlybuildin the same directory as the.gitignore. Without the slash,buildmatchesbuildat any depth, includingvendor/lib/build. - A slash anywhere in the middle also anchors:
doc/frotzbehaves like/doc/frotz. Only a pattern with no slash at all (or one solely at the end) floats to any depth. - Trailing slash = directories only:
build/ignores the directorybuildand everything under it, but never a regular file namedbuild. **crosses directory boundaries:**/foomatchesfooanywhere;abc/**matches everything insideabc;a/**/bmatchesa/b,a/x/b,a/x/y/b.?and[…]:?matches exactly one non-slash character;[Dd]esktop.iniand*.py[cod]are character classes, not regex alternation.!negates:!important.logre-includes a file an earlier pattern excluded. Later lines win over earlier ones.#starts a comment, and a blank line matches nothing (useful as a separator). Escape a literal leading#or!with a backslash:\#notacomment,\!important. A trailing space is stripped unless escaped as\.
The negation gotcha: it is impossible to re-include a file if a parent directory of that file is excluded. Git never descends into an ignored directory, so it never sees the file to reconsider it.
logs/ then !logs/keep.txt does not work. Exclude the contents instead of the directory: logs/* followed by !logs/keep.txt. For a deeper path you must un-ignore every intermediate level: /a/*, !/a/b/, /a/b/*, !/a/b/c.txt.
.gitignore does not untrack files Git already knows about
This is the single most common .gitignore problem. Ignore rules are consulted only when Git decides whether to show an untracked path. Once a file is in the index — because someone committed it before the rule existed, or added it with git add -f — adding a pattern changes nothing. The file keeps showing up in git status and keeps getting committed.
The fix is to remove it from the index while leaving it on disk. The --cached flag is what keeps your working copy intact:
# One specific file or directory git rm --cached .env git rm -r --cached node_modules/ # Or re-apply every ignore rule to the whole repo at once git rm -r --cached . git add . git commit -m "Apply .gitignore"
git rm -r --cached . empties the index, then git add . refills it — and this time the ignore rules apply, so the ignored paths are left out. The resulting commit shows the ignored files as deletions, which is expected: they are deleted from the repository, not from your disk. Commit any real work first, because staged changes are rewritten by this sequence.
To check why a specific path is or is not ignored, ask Git directly: git check-ignore -v path/to/file prints the exact file, line number, and pattern responsible. If it prints nothing, no rule matches and the file is tracked or simply not covered.
A secret that was ever committed is still in history. git rm --cached only removes it going forward. Anyone with the repo can read it from an older commit. Rotate the credential, then rewrite history with git filter-repo or BFG if the repo was published.
Which ignore file wins: precedence order
Git reads ignore patterns from several sources. For a given path it takes the highest-precedence source that has any matching pattern, and within that source the last matching line decides. Sources are listed here from highest to lowest precedence.
| Precedence | Source | Scope | Committed? |
|---|---|---|---|
| 1 (highest) | Command line (-e) | Single command | No |
| 2 | .gitignore (deepest dir) | That directory and below; nested files override parents | Yes — shared with the team |
| 3 | .git/info/exclude | This clone only | No — never leaves your machine |
| 4 (lowest) | core.excludesFile | Every repo for this user | No — global config |
Because nested .gitignore files sit above the repo-root one, a src/.gitignore containing !bundle.js re-includes a file that the root file ignored — as long as no parent directory is itself excluded.
Rule of thumb: put patterns everyone on the project needs (build output, dependency directories, secrets) in a committed .gitignore. Put your personal editor and OS junk in the global file so you do not force it on contributors:
git config --global core.excludesFile ~/.gitignore_global printf '.DS_Store\n.idea/\n*.swp\n' >> ~/.gitignore_global
Common patterns and what they match
| Pattern | Matches | Does not match |
|---|---|---|
| *.log | a.log, src/x/b.log | log, logs/a.txt |
| build/ | build/, app/build/out.js | build (a file) |
| /build | build at repo root | app/build |
| doc/*.txt | doc/a.txt | doc/sub/a.txt |
| doc/**/*.txt | doc/a.txt, doc/x/y/a.txt | a.txt |
| **/node_modules/ | node_modules/, pkg/a/node_modules/ | node_modules.bak |
| .env* | .env, .env.local, .env.example | env, config/.env |
| .env* !.env.example | .env, .env.local | .env.example (re-included) |
| *.py[cod] | a.pyc, a.pyo, a.pyd | a.py |
| [Dd]esktop.ini | Desktop.ini, desktop.ini | DESKTOP.INI |
| logs/* !logs/.keep | logs/app.log | logs/.keep (kept, so the dir survives) |
Git cannot track empty directories, which is why projects commit a placeholder such as .keep or .gitkeep and un-ignore it with !. The name has no special meaning to Git — it is a convention.
Why merging duplicates matters
Stacking templates produces a lot of repetition. Node, React, Vue, and Next.js all ship dist/ and .env.local; macOS, Ruby, and JetBrains templates each contribute their own overlapping junk. A naive concatenation of six templates commonly repeats 40–60 patterns.
Duplicates are harmless to Git — matching the same path twice changes nothing — but they make the file hard to audit, and they hide the one line that actually matters when you are debugging why a path is ignored. This generator keeps the first occurrence in a stable order, drops the rest, and reports the count so you can see how much overlap your stack has. Turn the toggle off if you prefer each ### Template ### block to stay verbatim and self-contained.
One thing dedupe deliberately does not do is reorder lines. Order is semantically meaningful in Git: a later ! negation overrides an earlier exclusion within the same file. Templates are emitted in a fixed sequence and lines keep their relative position, so a template's exclude/re-include pairs — like Yarn's .yarn/* followed by !.yarn/releases — still behave correctly.