DaleSchool

.gitignore and stash

Beginner20min

Learning Objectives

  • Understand and write .gitignore patterns (**, !, /)
  • Temporarily save and restore work with git stash
  • Manage multiple stashes and selectively restore them
  • Remove already-tracked files from Git tracking via .gitignore

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
  1. Create a .gitignore that ignores *.log, .env, and node_modules/.
  2. Run echo "KEY=secret" > .env and verify with git status that it's ignored.
  3. Start some work with echo "temp work" > temp.txt && git add temp.txt.
  4. Save it with git stash push -m "temp work saved" and confirm with git status that the tree is clean.
  5. Check git stash list and restore with git 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 .gitignore is enough