Use .gitignore to Exclude Files

Learn how to use .gitignore to keep unwanted files out of your Git repository. This lesson covers patterns, common use cases, and hands-on steps to create an effective .gitignore.

Focus: use .gitignore to exclude files

Sponsored

You just ran git add ., and suddenly your terminal is flooded with node_modules/, __pycache__/, or a .env file full of API keys. That is the moment you realize you need to use .gitignore to exclude files — before your secrets end up in a public repository or your repo bloats with junk. This lesson shows you exactly how to create a .gitignore file, write powerful patterns, and keep your repository clean and safe.

The problem this lesson solves

Every project has files that should never be committed: dependencies you can re-download, compiled artifacts you can rebuild, local configuration that differs per developer, and secrets that would be catastrophic if leaked. Without a .gitignore file, Git tracks everything in your working directory, so a single careless git add . can push megabytes of junk — or worse, your database credentials — into the history forever.

Even if you don’t commit secrets by accident, unignored clutter makes every clone slower, every diff noisier, and every code review harder. It also creates merge conflicts in files that should never be shared, like config.local.js. The fix is simple: use .gitignore to exclude files before they ever reach the index.

Core concept / mental model

Think of .gitignore as a bouncer at the door of your repository. It stands between your working directory and Git’s staging area. When you run git add, Git asks the bouncer: “Should I let this file in?” The bouncer checks the file’s path against its rules — if any rule matches, the file is turned away. The file stays on your disk, but Git never tracks it.

Three key terms to know:

  • Tracking: A file is tracked if Git knows about it and records its changes. Once tracked, .gitignore no longer applies to it.
  • Staging area: The middle ground where you prepare files for a commit. .gitignore prevents files from entering this area.
  • Pattern: A rule in .gitignore that uses wildcards and path matching to decide which files to exclude.

Pro tip: A common mental error is to think .gitignore deletes or hides files. It does not. It only tells Git, “don’t track me.” The file still exists locally and can be added with git add -f if you ever truly need it.

How it works step by step

Creating a solid .gitignore involves three steps: write the file, test it, and update it as your project evolves.

1. Create the .gitignore file

In your project root (or any subdirectory), create a plain text file named .gitignore. Git automatically recognizes it. The file uses one pattern per line. Blank lines are ignored; lines starting with # are comments.

2. Write patterns that match what you want to exclude

Patterns can be as simple as a filename (secret.txt) or as flexible as a glob (*.log). You can ignore entire directories with a trailing slash (build/). You can use negation with an exclamation mark (!important.log) to re-include a file that would otherwise be excluded.

3. Verify that the files are actually ignored

Run git status — ignored files should not appear as untracked. To see exactly which files are ignored, use git status --ignored or git check-ignore -v <file> to see which rule matched.

4. Update .gitignore as your project changes

Every new tool, dependency, or environment artifact may need a new rule. Keep your .gitignore under version control so every contributor benefits from the same exclusions.

Pro tip: If you use a framework-specific starter kit (like Create React App or Django), it ships with a pre-built .gitignore. Always review it and add your own project-specific entries.

Hands-on walkthrough

Let’s put it into practice. Create a fresh repository and add a few realistic file types.

# Actually, this is a shell example — use bash for Git commands.
# Create a new repo and some junk files
mkdir demo-gitignore
cd demo-gitignore
git init

# Create files we want to track and some we want to ignore
echo "print('hello')" > app.py
echo "secret key" > .env
touch debug.log
mkdir build

# Wait — creating a directory? Git only tracks files. We'll add a placeholder.
echo "build artifact" > build/output.bin

git status

Expected output (simplified):

Untracked files:
  .env
  app.py
  build/
  debug.log

Now create a .gitignore file:

echo ".env" > .gitignore
echo "build/" >> .gitignore
echo "*.log" >> .gitignore

# Check status again
git status

Expected output:

Untracked files:
  .gitignore
  app.py

app.py is now the only code file eligible for tracking. To prove the ignore works, run git status --ignored — you’ll see build/, .env, and debug.log listed under Ignored files.

Now let’s practice a negation pattern:

# Ignore all .log files, but keep one critical one
echo "!important.log" >> .gitignore
touch important.log

# Check which file is ignored vs tracked
git check-ignore -v debug.log
# Output: .gitignore:3:*.log    debug.log

git status --short important.log
# Output: ?? important.log

important.log shows as untracked (not ignored) because the negation rule re-included it.

Compare options / when to choose what

Sometimes .gitignore is not the only tool. Compare it with related mechanisms:

Tool Purpose When to use
.gitignore Exclude untracked files from the repository Use for files that should never be committed: dependencies, build output, local config, secrets
git update-index --skip-worktree Tell Git to ignore changes to a tracked file Use for files that must exist in the repo (e.g., config.example.json) but you want local edits ignored
git update-index --assume-unchanged Optimize performance by assuming a tracked file never changes Rarely needed; mostly for huge files that are slow to check
No ignore at all Track everything Only for toy repos or when every file is intentional

When to choose what:

  • Use .gitignore when the file should never be part of the repo.
  • Use skip-worktree when the file is part of the repo but you need to modify it locally without committing those changes.
  • Avoid assume-unchanged unless you fully understand its pitfalls — it can cause data loss if you forget the file is marked.

The .gitignore approach is the cleanest and most collaborative because the rules live in the repo itself.

Troubleshooting & edge cases

"I added a rule, but the file still shows as untracked"

This usually happens when the file is already tracked. As mentioned, .gitignore only applies to untracked files. To stop tracking an existing file, remove it from the index:

git rm --cached filename.txt

Then add the pattern to .gitignore. The file will remain on disk but become untracked.

"My negation pattern (!) isn’t working"

A classic mistake: if you ignore a whole directory (build/), you cannot re-include a file inside it (!build/keep.txt). Git will not re-include a file if its parent directory is excluded. The workaround is to unignore the directory first:

build/*
!build/keep.txt

Now keep.txt is included while everything else in build/ stays ignored.

"My .gitignore patterns are too broad and ignore files I need"

For example *.txt might ignore README.txt that you want tracked. Use more specific paths or add a negation rule. Prefer anchoring to a directory, like logs/*.txt, to keep scope tight.

"I accidentally committed a secret before adding the rule"

If the commit hasn’t been pushed, you can reset and amend. If it has been pushed, removing it from the index is not enough — the secret is still in the history. You must rotate the secret immediately. For rewriting history, tools like git filter-repo exist, but they are advanced and change all commit hashes.

What you learned & what's next

You now understand how to use .gitignore to exclude files from your Git workflow. You can create a .gitignore file, write basic and advanced patterns, test them with git check-ignore, and handle common edge cases like already-tracked files. This skill keeps your repo clean, your collaborators happy, and your secrets safe.

You’ve completed step 8 in the Git Tutorial. In the next lesson, you’ll learn how to undo mistakes using git revert and git reset — essential for when a .gitignore slip-up or a bad merge needs to be rolled back cleanly.

Practice recap

Create a small project, add files like secret.env and build/artifact.bin, and write a .gitignore that excludes them. Then try git status --ignored and git check-ignore -v to confirm each rule. Finally, create a negated pattern and verify it works, and if you have time, experiment with a global .gitignore for .DS_Store.

Common mistakes

  • Adding a rule to .gitignore after the file is already tracked — it has no effect until you run git rm --cached <file>.
  • Using git add . after creating .gitignore but forgetting to include the .gitignore file itself in the commit — then teammates won’t get the rules.
  • Attempting to negate a file inside an ignored directory (build/* + !build/keep.txt) without first unignoring the directory itself.
  • Forgetting that .gitignore patterns are case-sensitive and relative to the repo root; a pattern like *.Log won’t match debug.log.

Variations

  1. Use a global .gitignore (git config --global core.excludesFile) to exclude OS-specific files like .DS_Store from every repo without per-project clutter.
  2. Use .git/info/exclude for rules that are local to your clone only — useful for personal files that shouldn’t be shared with the team.
  3. Use git update-index --skip-worktree for tracked config files (e.g., config.local.json) where you want local changes ignored, instead of untracking the file.

Real-world use cases

  • Preventing node_modules/ from bloating a Node.js project — cloning and reinstalling is far faster than committing millions of files.
  • Keeping .env files with API keys, database credentials, and OAuth tokens out of public GitHub repos to avoid security breaches.
  • Excluding compiled build artifacts like dist/, __pycache__/, or Jupyter notebooks’ checkpoints from a data science repo to keep it lean.

Key takeaways

  • .gitignore is a plain text file with one pattern per line; it only affects untracked files.
  • Use patterns like *.log, build/, and !important.log to include or exclude specific files and directories.
  • Always commit .gitignore so every collaborator benefits from the same exclusion rules.
  • For already-tracked files, use git rm --cached to stop tracking before the ignore rule will apply.
  • Test your rules with git status --ignored and git check-ignore -v to verify behavior.
  • Never rely on .gitignore to secure secrets after a commit — rotate the secret and use history rewriting only if needed.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.