Git .gitignore
Keep generated files, environment secrets, and machine-specific noise out of history before they become expensive to remove.
Repository hygiene is part of the design
Define the repository boundary
The useful question is not “Can Git store this?” Git can store almost any file. Ask whether the file belongs in durable, shared, reviewable project history.
Source and decisions
Application code, documentation, migrations, reviewed configuration, and many dependency lockfiles usually belong in history.
Reproducible output
Compiled bundles, coverage reports, caches, and generated artifacts are often rebuilt from committed inputs.
Machine-local state
Editor settings, operating-system metadata, temporary files, and runtime logs may vary by contributor.
Credentials and private data
Tokens, private keys, live passwords, customer data, and production exports do not belong in ordinary source history.
What .gitignore actually does
A .gitignore file gives Git path patterns for intentionally untracked files. It reduces status noise and helps broad staging commands avoid known local output. The file itself should normally be committed so collaborators inherit the same repository policy.
Control untracked discovery
- Hides matching untracked paths from normal status.
- Prevents ordinary add operations from selecting them.
- Shares project-specific rules when committed.
- Supports comments, negation, and directory patterns.
Secure or erase content
- Does not encrypt files.
- Does not stop tracking an indexed path.
- Does not remove content from old commits.
- Does not revoke a leaked credential.
Read the pattern language precisely
*.log
Matches names ending in .log at any level below this ignore file.
/dist/
The leading slash anchors the rule to this ignore file’s directory; the trailing slash limits it to directories.
temp/
With no leading slash, matching directories named temp can be ignored below this level.
!keep.log
A later negation re-includes a path matched earlier, when Git can still traverse its parent directories.
docs/**/*.tmp
Double-star spans directories, so temporary files nested anywhere under docs match.
\#notes
An escaped leading hash matches a filename beginning with #; an unescaped hash starts a comment.
Rules are evaluated in order, and the last matching pattern decides the result. Begin with the narrowest pattern that expresses your policy. A broad rule such as *.json can hide package manifests, configuration, and data that should be reviewed.
Test what you learned
Write a pattern that ignores .log files anywhere below the .gitignore file.
Separate secret values from safe documentation
Applications often read environment variables from local files. The real values remain local; a committed example documents the required variable names and non-secret placeholders.
.env
DATABASE_URL=postgres://real-user:real-password@host/db
PAYMENT_API_KEY=live_private_valueContains working credentials. Never paste real values into training repositories, screenshots, issues, or chat.
.env.example
DATABASE_URL=postgres://USER:PASSWORD@HOST/DATABASE
PAYMENT_API_KEY=replace_with_local_keyDocuments names and expected shape without granting access to a real service.
Build a safe practice repository
Create this disposable tree in an empty repository. Use fake values only:
ignore-lab/
├── README.md
├── .env ← real local values: never commit
├── .env.example ← safe variable names and placeholders
├── app.log ← runtime noise
├── dist/
│ └── bundle.js ← reproducible build output
├── node_modules/
│ └── package/
└── src/
└── app.js ← source codeBefore writing ignore rules, run git status --short. Seeing all paths first makes the policy deliberate rather than copied from a template you do not understand.
Write a reviewed .gitignore
# Local environment files may contain credentials
.env
.env.*
!.env.example
# Runtime logs
*.log
# Root build output and installed dependencies
/dist/
/node_modules/The order matters: .env.* also matches .env.example, so the later !.env.example restores the safe template. Root-anchored dependency and build directories avoid silently hiding a same-named directory deeper in source.
Prove each rule works
git status --short --ignored
git check-ignore -v .env app.log dist/bundle.js
git check-ignore -v .env.example
git add .gitignore .env.example README.md src/app.js
git diff --cached
git statusSTATUSInclude ignored evidence--ignoredshows ignored paths as!!; normal short status leaves them quiet.CHECKTrace the matching patterncheck-ignore -vreports the source file, line, pattern, and path that matched.EXAMPLEConfirm the negation ruleVerbose output should name
!.env.example. The leading exclamation proves the later rule re-included the safe template.STAGESelect the public baselineThe cached diff should contain source, documentation, the example, and the ignore policy—never live values.
Test what you learned
Which command identifies the ignore file, line, and pattern matching .env?
Choose the right scope
Repository .gitignore
Committed, project-specific policy useful to every contributor: build output, project caches, and expected local environment files.
.git/info/exclude
Uncommitted rules for one clone. Useful for local scratch files that are not team policy.
Global excludes file
Personal rules across repositories, configured with core.excludesFile, such as operating-system or editor noise.
Nested .gitignore
Rules apply from that directory downward, allowing a component to own focused policy near its files.
Do not add every personal editor preference to the project file. Shared ignore rules should describe the repository, while global and local excludes describe your machine or workflow.
Ignored is not the same as untracked
If a path is already in the index, adding an ignore rule does not remove it. Git must continue reporting tracked changes, or ignore rules could silently hide edits to source files.
# Confirm whether Git already tracks the path
git ls-files --error-unmatch .env
# Stop tracking it, but keep the local file
git rm --cached .env
git commit -m "Stop tracking local environment file"Review the staged deletion before committing. --cached removes the path from the next snapshot while retaining the working copy. Once the ignore rule is present, the local file remains quiet.
If a secret enters history, respond in order
- 01Revoke or rotate immediately
Assume the value has been copied. Disable the credential at its provider and issue a replacement through an approved secret store.
- 02Assess exposure
Identify the credential, affected service, repository visibility, branches, tags, forks, logs, caches, and people or systems with access.
- 03Remove current tracking
Add a precise ignore rule, remove the file from the index, and replace it with safe documentation if needed.
- 04Coordinate history cleanup
Use an approved rewriting procedure and hosting-provider guidance. Rewriting changes commit IDs and requires collaborator coordination.
- 05Audit and prevent recurrence
Review access logs, notify the right people, add secret scanning, and document safe local setup.
Inspect before every broad commit
git status --short
git diff --cached --name-status
git diff --cached
# Useful questions:
# Do I recognize every staged path?
# Is any value a credential or personal record?
# Is generated output reproducible?
# Does the snapshot contain one coherent intention?Ignore rules reduce predictable noise, but they cannot classify every unexpected file. Human review and automated secret scanning are complementary safeguards.
Independent lab: design an ignore contract
Create a repository containing source, .env.local, .env.example, debug.log, coverage/, dist/, and docs/example.log. Then:
- Classify every path and justify whether it belongs in history.
- Write rules that ignore all local environment variants but restore
.env.example. - Ignore all logs, root coverage, and root build output.
- Use verbose check-ignore to prove at least four decisions.
- Stage only source, safe documentation, and
.gitignore. - Inspect the complete cached diff before committing.
- Explain why
docs/example.logis ignored and how you would re-include it if it were intentional documentation.
Common repository-hygiene mistakes
Copying a giant template blindly
Every pattern becomes policy. Remove rules you cannot explain and verify the paths that matter.
Ignoring after committing
Check tracking with git ls-files; ignore rules affect untracked discovery, not existing index entries.
Using force-add casually
git add -f overrides ignore protection. Use it only when the exception is understood and reviewed.
Confusing cleanup with rotation
Repository history work cannot make a leaked credential trustworthy again. Revoke it first.
Lesson review
You can now define what belongs in durable project history, express that boundary with precise patterns, prove why a path is ignored, distinguish ignored from tracked state, document environment variables safely, and respond to secret exposure without relying on false cleanup.
- I classify files before staging instead of treating every project path as source.
- I understand pattern anchoring, directory markers, wildcards, comments, and negation order.
- I verify ignore behavior with status and
git check-ignore -v. - I know why tracked files are unaffected by newly added ignore rules.
- I revoke exposed credentials before repository cleanup or history rewriting.