# How to Handle .gitignore in a Monorepo with Multiple Packages

> Master monorepo .gitignore strategies for multiple packages. Learn to combine root and package-specific files using GitHub templates for complete coverage and efficient version control.

- Repository: [GitHub/gitignore](https://github.com/github/gitignore)
- Tags: how-to-guide
- Published: 2026-02-24

---

**Combine a root `.gitignore` for global artifacts with package-specific `.gitignore` files in each sub-directory, using templates from the GitHub `gitignore` repository to ensure comprehensive coverage.**

Managing ignore patterns in a monorepo requires a hierarchical strategy that balances repository-wide rules with technology-specific exclusions. The `github/gitignore` repository provides battle-tested templates for every major language and tool, which you should adapt rather than writing rules from scratch. This approach prevents build artifacts, dependencies, and IDE files from polluting your git history while keeping each package's ignore logic modular and maintainable.

## Create a Root .gitignore for Global Artifacts

Start with a root-level `.gitignore` that captures files common to all packages in your monorepo. These typically include operating system files, editor workspace configurations, and monorepo-tooling logs that appear across multiple projects.

According to the `github/gitignore` source code, you should pull patterns from the **Global** templates for cross-cutting concerns:

- **OS-specific files** – Use patterns from `Global/macOS.gitignore` or `Global/Windows.gitignore` (e.g., `.DS_Store`, `Thumbs.db`)
- **IDE workspace files** – Reference `Global/VisualStudioCode.gitignore` for `*.code-workspace` and `Global/JetBrains.gitignore` for `.idea/**/workspace.xml`
- **Monorepo tooling** – The `Node.gitignore` template includes `lerna-debug.log*` for Lerna-based JavaScript monorepos

```gitignore

# Global OS artefacts

.DS_Store
Thumbs.db

# Editor / IDE workspace (do NOT commit)

*.code-workspace
.idea/**/workspace.xml

# Log files that appear in many tools

*.log

# Lerna (JS monorepo) temporary files

lerna-debug.log*

```

Keep this root file lean and generic. It should not contain language-specific build directories like `node_modules/` or `__pycache__/`, as those belong in package-specific files.

## Add Package-Specific .gitignore Files

Each package in your monorepo should maintain its own `.gitignore` file containing rules relevant only to that package's technology stack. This isolation ensures that a Python backend package doesn't inherit irrelevant Node.js patterns and vice versa.

Copy the appropriate template from `github/gitignore` directly into each package directory:

- **Node.js packages** – Copy from `Node.gitignore` to handle `node_modules/`, `npm-debug.log*`, and build outputs
- **Python packages** – Copy from `Python.gitignore` for `__pycache__/`, `*.py[cod]`, and virtual environment artifacts  
- **Rust packages** – Copy from `Rust.gitignore` for `target/` and `Cargo.lock` (in libraries)

Example for a `frontend/` package (React/Node.js):

```gitignore

# Node / npm artefacts (from Node.gitignore)

node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build output

build/
dist/

# Environment files

.env

```

Example for a `backend/` package (Django/Python):

```gitignore

# Python artefacts (from Python.gitignore)

__pycache__/
*.py[cod]
*.pyo
*.pyd
*.egg-info/
*.egg
*.whl

# Django specific

*.log
local_settings.py
db.sqlite3
/media
/staticfiles

```

## Use Conditional Includes for Reusable Rules

When multiple packages share identical technology stacks, duplicating `.gitignore` content violates the DRY principle. Git provides two mechanisms to apply reusable rule files conditionally.

### Option 1: Global Excludes File

Set a repository-wide excludes file that applies to all worktrees:

```bash
git config core.excludesFile .gitignore-global

```

Place common patterns (like those from `Global/VisualStudioCode.gitignore`) in this file to apply them everywhere without committing the configuration to each package.

### Option 2: Conditional Includes (Git 2.13+)

Use the `includeIf` directive in your `.gitconfig` to load technology-specific rules only when working within certain directories:

```gitconfig
[core]
    excludesfile = .gitignore-global

[includeIf "gitdir:frontend/"]
    path = .gitignore-node

[includeIf "gitdir:backend/"]
    path = .gitignore-python

```

Store `.gitignore-node` and `.gitignore-python` as copies of the corresponding `github/gitignore` templates in your repository root. Git automatically applies the correct rules based on your current working directory.

## Maintain and Verify Your Configuration

### Keep Templates Current

The `github/gitignore` repository updates continuously as new tools emerge. Refresh your package templates periodically using raw GitHub URLs to avoid cloning the entire repository:

```bash
curl -L https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore > packages/frontend/.gitignore
curl -L https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore > packages/backend/.gitignore

```

### Verify Pattern Application

After setup, confirm that your hierarchy works correctly using `git check-ignore`. This command identifies which `.gitignore` file is blocking a specific path:

```bash
git check-ignore -v packages/frontend/node_modules/some_pkg/index.js
git check-ignore -v packages/backend/__pycache__/module.cpython-38.pyc

```

The output displays the matching pattern and source file, allowing you to debug whether root or package-specific rules are catching the file.

## Summary

- **Root `.gitignore`** handles OS files, IDE configurations, and monorepo-wide logs using patterns from `Global/` templates in `github/gitignore`.
- **Package-specific files** use dedicated templates like `Node.gitignore` or `Python.gitignore` to handle build artifacts and dependencies local to each technology stack.
- **Conditional includes** via `git config` or `includeIf` directives eliminate duplication when multiple packages share the same stack.
- **Verification** with `git check-ignore -v` ensures your hierarchy applies rules exactly where intended.

## Frequently Asked Questions

### Can I use a single .gitignore file for the entire monorepo?

While technically possible using path-specific patterns (e.g., `**/node_modules/`), a single file becomes unmaintainable as your monorepo scales. Separate files allow packages to evolve independently—when you extract a package to its own repository, its `.gitignore` travels with it. The `github/gitignore` templates are also designed to be standalone, making per-package copies the path of least resistance.

### How do I handle nested packages with different technologies?

Nest `.gitignore` files at every level where the technology changes. Git aggregates rules from all `.gitignore` files in the path to a given file, with the nearest file taking precedence. For a `packages/mobile/ios/` directory containing a Swift project, place an iOS-specific `.gitignore` (from `Swift.gitignore`) inside that directory even if `packages/mobile/` already contains a React Native `.gitignore`.

### Should I commit the .gitignore templates or download them at build time?

Always commit the `.gitignore` files to your repository. These files stabilize your project structure and ensure every contributor ignores the same artifacts regardless of their local Git configuration. While you might use `curl` to initially populate or update the files, the committed versions serve as the source of truth for your team's development environment.

### What happens when two .gitignore files conflict?

Git processes `.gitignore` files from the repository root down to the target file's directory, with later rules overriding earlier ones. A negation pattern (starting with `!`) in a package-level file can re-include a file ignored by the root. Use `git check-ignore -v` to see exactly which pattern is matching when troubleshooting conflicts between global and package-specific rules.