Ignoring Files with .gitignore
Not every file in a project folder belongs in Git. Downloaded dependencies such as node_modules can be hundreds of megabytes, log files change every minute, your editor and operating system create junk files, and files like .env hold passwords that must never be shared. A .gitignore file tells Git to pretend these files do not exist.
In this lesson you will write a .gitignore, learn its pattern rules, check why a file is ignored, and fix the common situation where a file was committed before you ignored it.
The problem
Priya's sweet-shop project has grown. It now has npm packages, a log file, a secrets file and some junk files. Look at the status:
git status -s?? .env ?? Thumbs.db ?? logs/ ?? node_modules/ ?? notes.tmp
None of these should be committed. With so many untracked files, real changes get lost in the noise, and one careless git add . would publish the database password in .env.
Writing a .gitignore
A .gitignore is a plain text file, usually in the root of the repository, with one pattern per line. Priya creates this one:
# Dependencies installed by npm
node_modules/
# Secrets: never commit these
.env
# Log files, but keep the empty folder
logs/*
!logs/.gitkeep
# Editor and OS junk
*.tmp
Thumbs.db
.DS_StoreNow the status is clean apart from the two files she does want to commit:
git status -s -uall?? .gitignore ?? logs/.gitkeep
The .gitignore file itself should be committed, so everyone who clones the project gets the same rules:
git add .gitignore logs/.gitkeep
git commit -m "Add .gitignore and keep logs folder"[main eee3426] Add .gitignore and keep logs folder 2 files changed, 15 insertions(+) create mode 100644 .gitignore create mode 100644 logs/.gitkeep
Git does not track empty folders, only files. That is why people put a small placeholder such as .gitkeep inside a folder they want to keep. The name is only a convention; Git gives it no special meaning.
Pattern rules
| Pattern | Matches |
|---|---|
notes.txt | A file or folder called notes.txt anywhere in the project |
*.log | Any name ending in .log, in any folder |
build/ | A folder called build anywhere (the trailing / means folders only) |
/config.ini | config.ini only in the same folder as the .gitignore |
docs/*.pdf | PDF files directly inside docs, not in its subfolders |
**/temp | temp in any folder at any depth |
!important.log | Not ignored, even though an earlier rule matched it |
# text | A comment |
Two rules trip people up. First, later lines win, so a ! exception must come after the rule it overrides. Second, Git cannot re-include a file if its parent folder is ignored. That is why the example uses logs/* (ignore everything inside logs) instead of logs/ (ignore the folder itself): with logs/, the !logs/.gitkeep line would have no effect.
You can also put a .gitignore inside a subfolder; its patterns then apply relative to that folder.
Why is this file ignored?
When a file is ignored and you do not know why, ask Git. git check-ignore -v prints the file, line number and pattern responsible:
git check-ignore -v .env logs/app.log notes.tmp logs/.gitkeep.gitignore:5:.env .env .gitignore:8:logs/* logs/app.log .gitignore:12:*.tmp notes.tmp .gitignore:9:!logs/.gitkeep logs/.gitkeep
The last line shows the ! exception matched, which means logs/.gitkeep is not ignored. To see every ignored file in the project, use git status --ignored -s; ignored entries are marked with !!.
Ignoring a file that is already tracked
.gitignore only affects untracked files. If a file is already committed, Git keeps tracking it no matter what the ignore file says. Priya once committed settings.local.ini, her personal settings. She adds it to .gitignore, changes the file, and Git still reports it:
git status -sM .gitignore M settings.local.ini
The fix is to remove the file from the index while keeping it on disk, with --cached:
git rm --cached settings.local.ini
git commit -am "Stop tracking local settings file"rm 'settings.local.ini' [main 2c0ebc4] Stop tracking local settings file 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 settings.local.ini
The file is still in Priya's folder, and from now on Git ignores it. Teammates who pull this commit will have the file deleted from their working tree, so warn them to back it up first if it matters.
Global and private ignore rules
Some junk comes from your computer, not the project: Thumbs.db on Windows, .DS_Store on macOS, editor folders such as .idea/. Instead of adding these to every project, put them in a personal ignore file and tell Git about it once:
git config set --global core.excludesFile "~/.gitignore_global"For rules that apply to only one repository on your machine and should not be shared, add them to .git/info/exclude, which works like .gitignore but is never committed.
You do not have to write .gitignore files from scratch. When you create a repository on GitHub, you can pick a starter template for your language (Node, Python, Java and many more), and GitHub publishes its collection of templates in the github/gitignore repository.
Common mistakes
- Adding a rule after the file was committed and wondering why Git still tracks it. Run
git rm --cached <file>. - Using
folder/with a!exception inside it. Ignore the contents withfolder/*instead. - Ignoring
.gitignoreitself. Commit it so the whole team shares the rules. - Committing
.env"just for now". Secrets in history are leaked secrets. Commit an example file such as.env.examplewith fake values instead.
Key points
.gitignorelists patterns of untracked files Git should ignore; commit it with the project.*matches any characters, a trailing/matches folders only, a leading/anchors to the.gitignorefolder, and!re-includes a file.git check-ignore -v <file>explains which rule ignores a file.- Ignore rules do not affect tracked files; use
git rm --cachedto stop tracking one. - Personal rules go in a global excludes file or
.git/info/exclude.
Exercise
Write a .gitignore for a small Python project that should ignore: the .venv folder, all __pycache__ folders, every .pyc file, a secrets.json file in the project root only, and all .csv files in a data folder except data/sample.csv. Test one rule with git check-ignore -v.
Show solution
.venv/
__pycache__/
*.pyc
/secrets.json
data/*.csv
!data/sample.csvgit check-ignore -v data/march.csv.gitignore:5:data/*.csv data/march.csv
/secrets.json ignores only the root file, and the ! line comes after data/*.csv, so the sample file stays tracked. Learn more about .venv in pip & Virtual Environments.
Quick Quiz
1. You add config.php to .gitignore, but Git still shows changes to it. Why?
Run git rm --cached config.php and commit, then the ignore rule applies.
2. Which pattern ignores everything inside logs but lets you keep logs/.gitkeep with a ! rule?
If the folder itself is ignored, Git cannot re-include files inside it.
3. What does git check-ignore -v file show?
It is the quickest way to debug why a file is ignored.
4. You accidentally pushed an API key. What should you do first?
Anyone can read old commits, so the secret must be treated as leaked.
Learn Git with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
