Working Code
Example 1: Writing a basic .gitignore
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
# Environment variables (never commit these!)
.env
.env.local
.env.*.local
# Build output
dist/
build/
*.min.js
# Editor settings
.vscode/
.idea/
*.swp
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
EOF
Verify:
git add .gitignore
git commit -m "chore: add .gitignore"
Create an .env file:
echo "API_KEY=secret123" > .env
git status
Output:
nothing to commit, working tree clean
The .env file is ignored.
Example 2: Temporarily saving work with stash
# Working on something...
echo "Unfinished feature" > feature.txt
git add feature.txt
# Urgent bug fix needed!
git stash
git status
Output:
nothing to commit, working tree clean
After fixing the bug, restore your work:
git stash pop
git status
Output:
Changes to be committed:
new file: feature.txt
Try It Yourself
.gitignore pattern syntax in detail
# Basic patterns
*.log # All .log files (recursive)
!important.log # Exception: don't ignore important.log
# Slash (/) meaning
/TODO # Only TODO at the root
TODO # TODO in any directory
build/ # Ignore build directory (trailing slash = directory)
doc/ # doc directory and everything inside it
# ** (glob pattern)
**/*.log # .log files in all subdirectories
**/node_modules # All node_modules directories
doc/**/*.txt # All .txt files under doc
# Specific files
.env # Exact filename
.env.* # Everything starting with .env.
.env.local
.env.production
! exception pattern:
# Ignore all .txt files
*.txt
# But allow README.txt
!README.txt
Note: You cannot use ! to un-ignore files inside an already-ignored directory.
Managing multiple stashes
# Stash with a description
git stash push -m "login feature work in progress"
git stash push -m "design changes"
# List stashes
git stash list
Output:
stash@{0}: On main: design changes
stash@{1}: On main: login feature work in progress
# View a specific stash
git stash show stash@{1}
# View with full diff
git stash show -p stash@{1}
# Apply a specific stash (keep it in the list)
git stash apply stash@{1}
# Apply and remove a specific stash
git stash pop stash@{1}
# Drop a specific stash
git stash drop stash@{0}
# Clear all stashes
git stash clear
Create a new branch from a stash:
# Apply stash on a new branch
git stash branch feature-login stash@{0}
This creates a new branch from the commit where the stash was created and applies the stash.
"Why?" -- Why .gitignore matters
Files that should never be committed:
| Type | Reason |
| -------------------- | -------------------------------------------- |
| .env | API keys, passwords -- security risk |
| node_modules/ | Hundreds of MB, regenerated by npm install |
| *.log | Auto-generated, environment-specific |
| .DS_Store | macOS system file |
| dist/ | Auto-generated from source |
| .idea/, .vscode/ | Editor settings differ per developer |
If a secret key gets committed:
# If already committed:
# 1. Immediately revoke/rotate the key on GitHub/AWS/etc.
# 2. Remove from Git history (complex)
# 3. Replace with a new key
# Prevention is the best approach
echo ".env" >> .gitignore
Adding already-tracked files to .gitignore
.gitignore does not affect files that are already tracked:
# Adding .env to .gitignore alone is not enough
echo ".env" >> .gitignore
# Remove from tracking (file stays on disk)
git rm --cached .env
git commit -m "chore: stop tracking .env"
git rm --cached removes the file from Git's tracking index only -- the file itself remains on your disk.
Common Mistakes
Mistake 1: Adding .gitignore too late
# Starting without .gitignore, node_modules gets committed
git add .
git commit -m "init" # node_modules included!
# Adding .gitignore later won't help since files are already tracked
# Need: git rm --cached -r node_modules/
Creating .gitignore should be the very first thing you do in a new project.
Mistake 2: Confusing stash pop and apply
git stash pop # Apply + remove from stash list
git stash apply # Apply only, stash stays (useful for applying to multiple branches)
Mistake 3: Ignoring stash conflicts
git stash pop
# CONFLICT: conflict occurred
Stash pop can also trigger conflicts. After resolving, use git add and git stash drop to clean up.
Mistake 4: Adding already-committed files to .gitignore
# No effect
echo "secrets.txt" >> .gitignore
git status # secrets.txt is still tracked
# Correct approach
git rm --cached secrets.txt
git commit -m "chore: stop tracking sensitive file"
Deep Dive
Global .gitignore setup
Files to ignore across all projects:
git config --global core.excludesfile ~/.gitignore_global
cat > ~/.gitignore_global << 'EOF'
# macOS
.DS_Store
.AppleDouble
# Editors
.vscode/
*.swp
*.swo
.idea/
# Misc
*.log
.env
EOF
Auto-generate with gitignore.io
Generate language/environment-specific .gitignore files at gitignore.io:
curl -sL https://www.toptal.com/developers/gitignore/api/node,macos,vscode > .gitignore
Or with the gh extension:
gh extension install github/gh-gitignore
gh gitignore list
gh gitignore create Node macOS >> .gitignore
Stashing untracked files
By default, git stash only saves tracked files:
# Include untracked files
git stash push -u # --include-untracked
# Include ignored files too
git stash push -a # --all
- Create a
.gitignorethat ignores*.log,.env, andnode_modules/. - Run
echo "KEY=secret" > .envand verify withgit statusthat it's ignored. - Start some work with
echo "temp work" > temp.txt && git add temp.txt. - Save it with
git stash push -m "temp work saved"and confirm withgit statusthat the tree is clean. - Check
git stash listand restore withgit stash pop.
Q1. A file is already tracked by Git. You added it to .gitignore but it's still being tracked. How do you stop tracking it?
- A)
git ignore filename - B)
git rm --cached filename - C)
git remove filename - D) Adding it to
.gitignoreis enough