How Gitignore Works: .gitignore Rules, Patterns, And Examples
KEY TAKEWAYS:
- .gitignore controls untracked files only, so files already committed need index cleanup before ignore rules can take effect.
- Ignore rules are pattern-based and location-aware, with root-relative paths, directory patterns, wildcards, and negation rules all affecting matches.
- Good ignore files protect workflow quality by keeping secrets, dependencies, build output, caches, and local editor files out of shared history.
- Debugging starts with Git itself: check whether a file is tracked, confirm rule precedence, then use Git commands to explain the matching rule.
Understanding how gitignore works starts with one important boundary: Git ignore rules tell Git which untracked paths it should normally leave out of status and staging operations. A .gitignore file does not delete anything, and it does not make Git forget a file that is already tracked. Once that distinction is clear, the rest of the system – patterns, precedence, negation, local exclusions, and troubleshooting – becomes much easier to reason about.
Teams use ignore rules to keep generated output, dependencies, caches, local configuration, editor metadata, and machine-specific files out of a shared repository. Well-designed rules reduce noisy diffs and accidental commits without hiding source code that belongs under version control. This guide explains the model in practical terms, with examples you can test in a real repository.
Quick decision guide:
| What You Want To Do | Use This | Important Limit |
|---|---|---|
| Share ignore rules with every contributor | Commit one or more .gitignore files | Rules affect untracked paths, not files already in the index |
| Ignore a repository-specific file only on your computer | Add the pattern to .git/info/exclude | The rule stays local and is not cloned |
| Ignore editor or OS clutter in every repository | Configure core.excludesFile | Do not put project-wide rules here |
| Find the rule that matches a path | Run git check-ignore -v -- path | Tracked files are skipped unless you add --no-index |
| Stop tracking a file but keep the local copy | Run git rm --cached -- path, then commit | This changes the repository index for collaborators |
Recommended for you:
- What Are Git Concepts And Architecture?
- What Is Website Development? Basics For Beginners
- Web Development Services

What Is A .gitignore File In Git?
A .gitignore file is a plain-text list of patterns that identifies intentionally untracked files and directories. Git consults those patterns when commands such as git status and git add decide which untracked paths to show or stage. The Git documentation describes the purpose precisely: ignore rules are for paths that should remain untracked.
The file is usually placed at the repository root and committed so everyone receives the same baseline. A project can also contain nested .gitignore files. For example, a monorepo might keep general rules at the root while a package-specific file documents generated output inside one application. Each pattern is evaluated relative to the directory containing that particular ignore file.
The filename begins with a dot because it is a hidden file on Unix-like systems. That naming detail does not give it special security properties. It is simply one of the exclude sources Git knows how to read. You can edit it with any text editor, and it should be reviewed like source code because an overly broad rule can hide files that a contributor expected to commit.
Rule of thumb: ignore reproducible or machine-specific output; track the source, configuration templates, and documentation needed to rebuild the project.
A good file often groups rules by purpose and adds comments. That makes it clear whether an entry protects secrets, removes build noise, or accommodates a particular tool. If a team changes its framework or build process, those sections are easier to update than an unexplained list copied from a generic template.
It also helps to distinguish an ignored file from an untracked file. An untracked file is simply absent from the index. It may still appear in git status and can normally be staged. An ignored file is also untracked, but a matching exclusion tells common commands not to report or add it by default. This is a visibility and staging rule, not a different storage format. Git can still operate on the path when a command explicitly asks it to do so.

How Gitignore Rules Work In A Repository
Git can read exclusions from several sources. The source matters because the same path may match more than one pattern. According to Git’s precedence model, command-line patterns have the highest priority. Repository .gitignore files come next, followed by the repository-local exclude file and then the user-wide exclude file. Within one level, the last matching pattern determines the result.
| Rule Source | Scope | Best Use |
|---|---|---|
| Command-line exclude patterns | One supporting command invocation | Temporary or scripted filtering that should take highest precedence |
Repository .gitignore | The file’s directory and descendants | Shared rules that belong in version control |
Nested .gitignore files | A specific subtree | Rules owned by one package, module, or generated directory |
.git/info/exclude | One local clone | Personal repository-specific files that should not be shared |
Global gitignore file via core.excludesFile | All repositories for one user | Editor backups, OS metadata, and other personal tooling artifacts |
A repository normally commits its root .gitignore. If a nested file contains a pattern that also matches a root rule, the lower-level file can override the higher-level one for paths in its subtree. Within the same file, order also matters: a later negation can restore a previously ignored path, provided Git can still traverse the parent directory.
Use .git/info/exclude when the rule is relevant to one clone but not the project. Suppose you keep private scratch notes beside the source. Adding notes.local.md to this file hides it locally without forcing every contributor to accept that convention. GitHub’s guide to GitHub Docs recommends this approach for locally generated files that should not be committed.
For user-wide noise, configure a global ignore file:
git config --global core.excludesFile ~/.gitignore_globalThis is useful for editor backup files or operating-system metadata that follows you across projects. Keep framework output and team conventions in the repository instead. Otherwise, a clean status on your machine may depend on an invisible personal rule that no teammate has.
Precedence is easiest to understand with a concrete case. Imagine the root file ignores *.log, while tests/.gitignore contains !expected.log. Inside tests, the lower-level negation can keep that fixture visible. If the same negation appeared before *.log in one file, the later exclusion would win instead. This is why moving a rule between files can change behavior even when the pattern text is identical.
Further reading:
- DevOps Tools: Best Tools For Every Stage Of The DevOps Lifecycle
- What Is A CI/CD Pipeline? How Modern Teams Turn Code Into Reliable Releases
- DevOps Best Practices: Ways To Improve Speed And Reliability

Common .gitignore Patterns And Syntax Examples
Each nonblank line is normally a pattern. A leading # creates a comment, a trailing slash limits a match to directories, a leading slash anchors a pattern to the location of the ignore file, and a leading ! negates an earlier exclusion. Wildcards add flexibility, but their relationship with path separators matters.
| Pattern | What It Ignores | Example Use |
|---|---|---|
debug.log | A file or directory with that name at any depth below this ignore file | One recurring log filename |
logs/ | Directories named logs and their contents | Generated application logs |
*.log | Names ending in .log, except across a slash | All log files |
/dist/ | The root-level dist directory relative to this ignore file | One project build directory |
logs/**/*.log | Log files at any depth inside logs | Nested service logs |
!important.log | Re-includes a path ignored by an earlier pattern | Keep one meaningful fixture or sample |
\#report.txt | A literal filename beginning with # | Escape a character that otherwise starts a comment |
A single asterisk matches characters other than a slash. A question mark matches one non-slash character, and bracket expressions such as [0-9] match one character from a range. Two asterisks have special pathname behavior: **/temp can match at any depth, assets/** matches everything inside that directory, and a/**/b allows zero or more intermediate directories.
# Dependencies and generated outputnode_modules//dist/*.log # Ignore every environment file.env* # Keep the public example!.env.exampleNegation has a common trap. Git cannot re-include a file when its parent directory itself has been excluded, because excluded directories are not traversed for performance reasons. If you want to ignore most children but keep one, ignore the contents rather than the directory:
# This allows Git to inspect the directorylogs/* # Keep one reviewed example!logs/important.logComments should explain intent, not repeat the pattern. For example, # Generated API clients; rebuild with npm run generate tells the next maintainer why the directory is absent and how to restore it. Escape a leading # or ! with a backslash when it belongs to a literal filename. Trailing spaces are ignored unless escaped.
Anchoring deserves special attention. A bare cache/ can match directories named cache anywhere below the ignore file. By contrast, /cache/ targets only the directory at that ignore file’s level. A pattern containing an internal slash, such as docs/generated/, is already relative to that level. Prefer the narrowest form that represents your intent; broad name-only rules are convenient but can silently cover an unrelated directory added months later.

Why .gitignore Does Not Ignore Tracked Files
The index is Git’s proposed next snapshot. Once a file has been added to that index and committed, Git tracks its changes by path. Ignore rules are not designed to override this state. That is why adding .env to .gitignore after it has already been committed does not make its modifications disappear from git status.
To stop tracking a file while preserving your working copy, first add the appropriate rule and then remove the path only from the index:
git rm --cached -- .envgit commit -m "Stop tracking local environment file"For a directory, add -r: git rm -r --cached -- generated/. Review the staged deletion before committing because the next commit tells collaborators that the tracked version has been removed. The working copy remains on your computer when --cached is used. This behavior is documented in both Git ignore notes and the git rm manual.
The reverse is possible too. git add -f path force-adds an ignored path. That escape hatch is useful when a broad rule intentionally has one reviewed exception, although an explicit negation in the shared file is often clearer. Git’s git add documentation defines --force as allowing otherwise ignored files to be added.
Security warning: adding a secret to
.gitignoreprevents a future accidental add; it does not erase a credential from existing commits, forks, caches, or clones.
If a password, token, or private key was committed, revoke or rotate it first. Removing sensitive data from history is a separate, coordinated incident-response task. GitHub’s official guidance recommends git-filter-repo for history cleanup and warns that rewriting history changes commit hashes, affects pull requests, and can be recontaminated by old clones. Treat .gitignore as one preventive layer alongside secret managers, pre-commit scanning, careful staging, and push protection.
Teams sometimes use git update-index --assume-unchanged or --skip-worktree to quiet changes to tracked configuration. Those index flags are not replacements for ignore rules and can create confusing local state. A safer design is to track a sample such as .env.example, generate a local file during setup, and ignore only the local result. The repository then preserves the configuration contract without asking Git to pretend that tracked content did not change.
Related reading:
- What Is DevSecOps? Shift-Left Security In Modern DevOps
- AI Code Review: Tools And Best Practices For Implementation
- AI Coding Assistant Tools For Developers

Useful .gitignore Examples By Project Type
Templates are a starting point, not a substitute for understanding the build. Before copying a rule, ask whether the ignored path is generated, reproducible, local, or sensitive. If it is the only copy of required source or configuration, it probably belongs in Git. GitHub maintains a public collection of recommended templates, but every repository still needs a human review.
Node.js projects:
node_modules/.envdist/coverage/npm-debug.log*Dependencies can be restored from the package manifest and lockfile, so node_modules/ stays out while package.json and the chosen lockfile stay tracked. Whether dist/ should be ignored depends on deployment: some libraries intentionally commit distribution artifacts, while applications usually build them in continuous integration.
Python projects:
__pycache__/*.py[cod].venv/.env.pytest_cache/Virtual environments, bytecode, and test caches are local outputs. Track dependency declarations such as pyproject.toml, requirements.txt, or a lockfile so another developer can reproduce the environment.
Java projects:
target/*.class*.jar.gradle/build/Maven commonly writes to target/, while Gradle commonly uses build/ and maintains a local cache. Be careful with *.jar if the repository intentionally vendors a binary that cannot be fetched from a package registry.
Frontend frameworks:
build/.next/.cache/.nuxt/.vite/coverage/Framework output can be large and changes often. Excluding it keeps reviews focused on source code. The build command, runtime version, environment contract, and deployment workflow should remain documented so ignored artifacts can be reproduced reliably within the broader software development life cycle.
Operating systems and editors:
.DS_StoreThumbs.db.idea/.vscode/*.swpPersonal editor metadata is a good candidate for a global exclude file. However, some teams intentionally track selected files under .vscode/, such as recommended extensions or shared launch configurations. An all-or-nothing rule can discard that useful collaboration layer, so use negation or list only private subfiles.
Generated files also deserve a deliberate policy. Committing a generated client or compiled asset can be valid when consumers cannot build it, releases require it, or review policy treats generated diffs as auditable deliverables. In that case, do not ignore it merely because a template says to. Document who regenerates it, which command is authoritative, and how CI detects drift. Ignore decisions should follow the repository’s delivery model, not the language name alone.

How To Fix Gitignore Not Working
When a path still appears or disappears unexpectedly, do not guess. Work through the repository state and the matching rules in order. The fastest diagnostic is usually git check-ignore, whose verbose mode prints the source file, line number, matching pattern, and path.
A Five-Step Gitignore Diagnostic Flow
1. Name the path
Use its repository-relative spelling and confirm case.
2. Find the rule
Run git check-ignore -v -- path.
3. Check tracking
Run git ls-files --error-unmatch -- path.
4. Fix the cause
Edit the pattern or remove the path from the index.
5. Verify
Repeat the check and review git status.
Start with:
git check-ignore -v -- path/to/file.logIf the path is ignored, the output identifies the winning pattern. The git check-ignore manual notes that tracked files are not shown by default because exclude rules do not apply to them. Use --no-index when you specifically need to test ignore matching without consulting the index.
- Confirm the file is not already tracked. Run
git ls-files --error-unmatch -- path. A successful match means you need an index change, not a stronger ignore pattern. - Use
git rm --cachedcarefully. Remove only the intended path from the index, inspect the staged change, and commit the transition. - Read patterns from top to bottom. Within the same precedence level, the last matching rule wins. A late negation or broad wildcard may explain the result.
- Inspect parent directories. You cannot restore a file from an entirely excluded parent directory. Change
folder/tofolder/*if Git needs to examine children. - Check every source. Look at root and nested
.gitignorefiles,.git/info/exclude, and the path set bygit config --get core.excludesFile. - Confirm spelling and location. Patterns are relative to their ignore file. Case behavior can also vary with the filesystem and repository configuration.
- Look for force-added paths. A contributor may have used
git add -f, which places an ignored path in the index.
When debugging a nested project, run commands from the working tree and pass an unambiguous path. Avoid repeatedly deleting caches or rebuilding before you know which state is wrong. The command output usually tells you whether the problem is matching, precedence, or tracking.
There is one more useful interpretation rule: no output from git check-ignore normally means no ignore pattern matched, not that the command failed. Add -v -n when inspecting many paths and you need non-matches represented explicitly. For scripts, rely on the documented exit status rather than parsing a human-oriented message. These small habits make the same troubleshooting process dependable in a local shell and in automated repository checks.
Explore more:
- Web Development Tutorials To Build Your Career
- What Is JavaScript? How It Makes Websites Interactive
- JavaScript Frameworks Worth Attention

Clean Git Workflows Start With The Right Ignore Rules
Ignore rules are a small part of repository governance, but they influence every commit. A consistent file reduces accidental changes, keeps pull requests readable, and makes local development closer to continuous integration. It also creates a visible contract: contributors can see which outputs are disposable and which assets must remain recoverable.
The strongest workflow combines narrow patterns with a documented repository structure. Teams should review ignore changes, keep environment examples separate from real credentials, verify build output in CI, and make staging deliberate. That discipline fits naturally into a repeatable web development process where source, testing, deployment, and maintenance responsibilities are explicit.
A good .gitignore is part of the repository contract, not personal editor housekeeping. In our delivery work, an ignore-rule review checks that secrets never enter history, generated artifacts are reproducible in CI, required configuration examples remain tracked, and deployment does not depend on an uncommitted local file. This broader software development process turns a clean git status into evidence that another contributor or automation runner can reproduce the project.
In short, how gitignore works is predictable once you separate three questions: is the path tracked, which exclude source has precedence, and what is the last matching pattern at that level? Use shared rules for the team, local rules for personal artifacts, global rules for machine-wide clutter, and git check-ignore -v whenever the answer is unclear.
Review the file whenever dependencies, frameworks, deployment targets, or editor conventions change. Remove obsolete entries, tighten rules that hide too much, and test representative paths before merging. A short, intentional ignore file is easier to trust than a huge template whose entries nobody owns. That trust matters because a clean working tree is most useful when developers know it reflects the real source state rather than hidden exceptions.
Continue reading:
- Agile Software Development Life Cycle Guide
- Scrum Software Development Turns Change Into Working Software
- Software Project Management Methodologies For Software Development

FAQs About How Gitignore Works

Does .gitignore Remove Files Already Committed?
No. A .gitignore rule does not delete a file and does not stop Git from tracking a path already in the index. To keep the local file but remove it from future snapshots, add the rule, run git rm --cached -- path, review the staged deletion, and commit it. If the file contained a secret, rotate the credential and treat history cleanup as a separate security process.
Where Should A .gitignore File Be Placed?
Most projects place a committed .gitignore at the repository root so its rules apply throughout the working tree. You can add nested files when a subdirectory needs local ownership or more specific rules. Patterns are evaluated relative to the directory containing each ignore file.
What Is The Difference Between .gitignore And .git/info/exclude?
A .gitignore file is normally committed and shared with everyone who clones the repository. .git/info/exclude belongs to one local clone and is not committed. Use the first for project rules and the second for personal repository-specific files that teammates do not need to ignore.
How Do I Ignore A File Only On My Computer?
Add the pattern to .git/info/exclude if it applies only to the current repository. If the same type of editor or operating-system file should be ignored across all your repositories, place it in the file configured by core.excludesFile. Neither choice should replace shared project rules.
Can I Use Multiple .gitignore Files In One Repository?
Yes. Git reads .gitignore files from the path’s directory and its parents up to the repository root. A lower-level file can provide more specific rules for its subtree, and its matching patterns override higher-level files. This is useful in monorepos, but excessive nesting can make behavior harder to trace, so document the reason and use git check-ignore -v to debug the winner.
Related Articles

