Why ignore files?Your project directory will almost always contain files that don't belong in your Git repository. Build artifacts, dependency folders, secret configuration, editor settings, OS-generated junk — committing any of these creates noise for collaborators and can even leak credentials.Common examples include: Dependency directories installed by package managers (node_modules/, vendor/, venv/) Build output generated by compilers or bundlers (dist/, build/, *.o, *.pyc) Environment and secret files containing API keys or database credentials (.env, credentials.json) OS files that your operating system creates automatically (.DS_Store, Thumbs.db) Editor and IDE folders with personal workspace settings (.vscode/, .idea/, *.swp) Logs and error reports generated at runtime (*.log, npm-debug.log*) You could just avoid staging these files, but that's error-prone and they'll still clutter git status output. A better approach is to tell Git to ignore them automatically.Creating a .gitignore fileCreate a file called .gitignore in the root of your repository. Inside it, list patterns — one per line — that describe which files and directories Git should ignore.# Create the file touch .gitignore # Open it in your editor and add patterns Once you've saved your .gitignore, commit it to the repository so the rules are shared with everyone who works on the project:git add .gitignore git commit -m "Add .gitignore" Tip: You should commit your .gitignore early — ideally in the first commit of a new project, before you accidentally commit files you don't want tracked.Pattern syntaxEach line in a .gitignore file is a pattern. Here are the rules Git uses to match them:Basic patterns Pattern What it matches config.yml Any file or directory named config.yml, at any depth *.log Any file ending in .log debug?.log debug0.log, debuga.log, etc. (? matches one character) debug[0-9].log debug0.log through debug9.log Directory patternsAppend a trailing slash to match directories only: Pattern What it matches logs/ Any directory named logs, at any depth build/ Any directory named build, at any depth Anchoring with a leading slashWithout a leading slash, a pattern matches at any level in the directory tree. Adding a leading / anchors the pattern to the directory where the .gitignore file lives: Pattern What it matches config.yml config.yml, src/config.yml, app/config.yml /config.yml Only config.yml in the project root /build/ Only the build/ directory in the project root Double asterisk (**)The ** wildcard matches across directory boundaries: Pattern What it matches **/logs logs, src/logs, app/src/logs **/logs/*.log logs/debug.log, src/logs/error.log src/** Everything inside src/, at any depth a/**/b a/b, a/x/b, a/x/y/b Negation (exceptions)Prefix a pattern with ! to re-include a file that would otherwise be ignored:# Ignore all .env files *.env # But keep the example file !.env.example Important caveat: You cannot re-include a file if its parent directory is already ignored. Git skips ignored directories entirely for performance, so it never looks inside them:# This will NOT work as expected build/ !build/important.js To work around this, ignore the directory's contents instead of the directory itself:# Ignore everything in build/ but allow exceptions build/* !build/important.js Comments and blank linesLines starting with # are comments. Blank lines are ignored and can be used to organise your patterns into logical groups:# Dependencies node_modules/ vendor/ # Build output dist/ build/ # Environment .env .env.local Real-world .gitignore examplesNode.jsnode_modules/ dist/ build/ .env .env.local npm-debug.log* yarn-debug.log* yarn-error.log* .yarn/cache .yarn/unplugged coverage/ Python__pycache__/ *.py[cod] *.so venv/ .env *.egg-info/ dist/ build/ .pytest_cache/ htmlcov/ .coverage Rubyvendor/bundle/ .bundle/ *.gem Gemfile.lock log/ tmp/ .env coverage/ PHP / Laravelvendor/ node_modules/ .env storage/*.key public/hot public/storage *.cache WordPresswp-content/uploads/ wp-content/cache/ wp-content/upgrade/ wp-config.php .htaccess *.log General patterns (add to any project)# OS files .DS_Store Thumbs.db Desktop.ini # Editors and IDEs .vscode/ .idea/ *.swp *.swo *~ .project .classpath .settings/ # Misc *.bak *.tmp Tip: The github/gitignore repository maintains a comprehensive collection of .gitignore templates for virtually every language and framework. It's a great starting point when setting up a new project.Ignoring files that are already trackedThis is the most common gotcha with .gitignore: it only affects untracked files. If you've already committed a file, adding it to .gitignore won't make Git forget about it.To stop tracking a file that's already been committed:# Remove from Git's index but keep the file on disk git rm --cached .env # For an entire directory git rm -r --cached node_modules/ # Then commit the removal git commit -m "Stop tracking .env" After this, the file stays on your local machine but Git treats it as untracked, and your .gitignore pattern will now take effect.Warning: When other team members pull this commit, the file will be deleted from their working directory. If it's something like .env, make sure everyone knows to back up their copy first, or provide a .env.example template in the repo.Global .gitignoreSome files — like .DS_Store on macOS or Thumbs.db on Windows — should be ignored everywhere, not just in one project. Instead of adding them to every repository's .gitignore, configure a global ignore file:# Create the global ignore file touch ~/.gitignore_global # Tell Git to use it git config --global core.excludesFile ~/.gitignore_global A typical global .gitignore contains patterns for your OS and editor:# macOS .DS_Store .AppleDouble .LSOverride # Windows Thumbs.db ehthumbs.db Desktop.ini # Linux *~ .directory # Editors .vscode/ .idea/ *.swp *.swo *.sublime-workspace *.sublime-project The global file applies to every repository on your machine, but it is not committed anywhere — it only affects you. Project-specific patterns should still go in the repository's .gitignore so they're shared with collaborators.Local-only rules with .git/info/excludeIf you need to ignore files in a specific repository but don't want to modify the shared .gitignore, you can add patterns to .git/info/exclude. This file works exactly like .gitignore but is never committed.This is useful for personal tooling or editor files that only affect your workflow:# Add patterns directly echo "my-scratch-notes.txt" >> .git/info/exclude echo ".env.local.bak" >> .git/info/exclude Nested .gitignore filesYou can place a .gitignore file in any subdirectory of your repository, not just the root. Patterns in a nested .gitignore are relative to the directory where the file lives, and they only apply from that directory downward.project/ ├── .gitignore # Project-wide rules ├── docs/ │ └── .gitignore # Rules specific to docs/ └── src/ └── .gitignore # Rules specific to src/ When multiple .gitignore files exist, the rules from the most specific (deepest) file take precedence. In practice, most projects only need a single root-level .gitignore. Use nested files when a subdirectory has meaningfully different ignore requirements — for example, a docs/ folder that generates HTML output you don't want tracked.Debugging with git check-ignoreWhen a file is being ignored and you can't figure out why — or it's not being ignored and you think it should be — use git check-ignore to debug:# Check if a file is ignored and show which rule matched git check-ignore -v path/to/file Output example:.gitignore:3:*.log debug.log This tells you that line 3 of .gitignore (the pattern *.log) is causing debug.log to be ignored.To check multiple files at once:git check-ignore -v *.log src/*.tmp If the command produces no output, the file is not being ignored by any rule.Keeping empty directoriesGit only tracks files, not directories. If you need an empty directory to exist in the repository — for example, a logs/ folder that should be present but always empty — the convention is to add a .gitkeep file inside it:mkdir logs touch logs/.gitkeep Then in your .gitignore:# Ignore log files but keep the directory logs/* !logs/.gitkeep Note: .gitkeep is not a Git feature — it's a community convention. The file name doesn't matter; it could be called anything. .gitkeep is simply the most widely recognised name for this purpose.Quick reference I want to... Pattern Ignore all .log files *.log Ignore node_modules/ everywhere node_modules/ Ignore .env only in the root /.env Ignore everything in build/ except one file build/* then !build/keep.js Ignore all .tmp files in any cache/ folder **/cache/*.tmp Ignore a directory but keep it in the repo Add dir/* + !dir/.gitkeep Stop tracking a file already committed git rm --cached Find out why a file is ignored git check-ignore -v Ignore files globally (all repos) git config --global core.excludesFile ~/.gitignore_global Once your .gitignore is set up and your repository is clean, DeployHQ can deploy your code automatically — only the files that matter reach your server, while ignored files stay out of the deployment.