Ignoring Files
Every project produces files that shouldn't be in version control: build output, downloaded dependencies, log files, editor settings, and secrets such as passwords and API keys. A .gitignore file lists them, so Git stops reporting them as untracked and git add . never picks them up.
The problem
Sam's online shop project has generated folders, logs, a secrets file and a stray system file lying around:
git status -s
?? .DS_Store ?? .env ?? debug.log ?? dist/ ?? logs/ ?? node_modules/
None of these belong in history. The dependencies can be reinstalled, the build can be rebuilt, the logs are noise — and the .env file contains a live API key that must never be committed.
Creating .gitignore
Create a file called .gitignore in the top folder of the repository, with one pattern per line:
cat > .gitignore <<'EOF' # Dependencies and build output node_modules/ dist/ # Logs *.log # Secrets .env # Operating system files .DS_Store EOF git status -s
?? .gitignore
Only .gitignore itself is left. Commit it — the rules then apply to everyone who works on the project:
git add .gitignore
git commit -m "Ignore dependencies, build output, logs and secrets"
[main 1f7c5de] Ignore dependencies, build output, logs and secrets 1 file changed, 12 insertions(+) create mode 100644 .gitignore
Pattern rules
| Pattern | Ignores |
|---|---|
debug.log | A file (or folder) called debug.log in any folder |
*.log | Every file ending in .log, anywhere |
build/ | Folders called build (the trailing slash means "folders only") |
/todo.txt | Only todo.txt in the top folder (a leading slash anchors the pattern) |
docs/*.pdf | PDF files directly inside docs, not in its subfolders |
docs/**/*.pdf | PDF files anywhere under docs |
!keep.log | Exception: don't ignore keep.log, even though *.log matched it |
# comment | Nothing — lines starting with # are comments |
One catch with !: you can't re-include a file if its parent folder is ignored, because Git doesn't look inside ignored folders at all. Ignore the folder's contents (logs/*) instead of the folder itself (logs/) when you need exceptions.
cat >> .gitignore <<'EOF' !logs/README.log EOF echo "Logs are written here" > logs/README.log git status -s
M .gitignore ?? logs/
logs/ shows up as untracked again because of the exception: README.log is no longer ignored, while server.log next to it still is. This worked because only the files were ignored (by *.log). Had the rule been logs/, the exception would have had no effect.
Why is this file ignored?
git check-ignore -v reports the file and line number of the matching rule:
git check-ignore -v debug.log node_modules/express/index.js
.gitignore:6:*.log debug.log .gitignore:2:node_modules/ node_modules/express/index.js
A file that isn't ignored produces no output. To list every ignored file in the project, use git status --ignored -s (ignored files are marked !!).
Ignoring a file that's already tracked
.gitignore only affects untracked files. If a file was committed before you added the rule, Git keeps tracking it. Remove it from the repository — but not from your disk — with git rm --cached:
echo "settings.local.ini" >> .gitignore
git rm --cached settings.local.ini
rm 'settings.local.ini'
git add .gitignore
git commit -m "Stop tracking local settings"
[main 6e030c2] Stop tracking local settings 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 settings.local.ini
ls settings.local.ini
settings.local.ini
The file still exists locally, and Git now ignores it. (The old versions remain in history — which is why secrets need extra care, as described below.)
Other places for ignore rules
| Location | Use it for | Shared? |
|---|---|---|
.gitignore (any folder) | Project rules: build output, dependencies. A .gitignore in a subfolder applies to that folder. | Yes — committed |
.git/info/exclude | Your own files in one repository, like a personal notes file | No |
| Global ignore file | Things your computer or editor creates in every project: .DS_Store, Thumbs.db, .idea/, *.swp | No |
git config --global core.excludesFile ~/.gitignore_global
Starting points
You don't have to write rules for common tools from scratch. GitHub offers a choice of ready-made .gitignore files when you create a repository, and maintains a public collection of templates for most languages and frameworks (the github/gitignore repository). Start from one and adjust it.
If you commit a secret
Treat any password or key that reached a commit as leaked — especially once it's been pushed. First revoke or rotate the key at the service that issued it; that's the only real fix. Removing it from the latest commit doesn't remove it from history. Tools such as git filter-repo can rewrite history to delete it, but anyone who already cloned the repository still has a copy. GitHub scans public repositories for known key formats and may warn you or the provider.
The habit that prevents all of this: keep secrets in an ignored file such as .env, and commit a .env.example with the variable names and fake values so others know what to set.
Try it yourself
In a test repository, create a build folder, a notes.tmp file and an important.tmp file. Write a .gitignore that ignores the folder and all .tmp files except important.tmp, and confirm with git status -s and git check-ignore -v.