Gitignore vs Gitkeep: Tracking Empty Directories

Understand the difference between .gitignore and .gitkeep. Learn how to track empty directories in git and when to use each convention in your projects.

Pattern

Detailed Explanation

.gitignore and .gitkeep solve opposite problems: .gitignore tells git to exclude files, while .gitkeep is a convention to force git to track an otherwise-empty directory. Understanding both is essential for structuring your repository correctly.

The problem: git does not track empty directories

Git only tracks files, not directories. If you create an empty logs/ directory and run git add logs/, nothing happens. Git simply does not see it. This is by design — directories are implicitly created when files within them are checked out.

The .gitkeep convention:

.gitkeep is NOT a git feature. It is a community convention — an empty placeholder file added to a directory to force git to track it:

logs/.gitkeep
uploads/.gitkeep
tmp/.gitkeep

The filename .gitkeep has no special meaning to git. You could name it .keep, .placeholder, or README. The community settled on .gitkeep because it clearly communicates the intent.

When to use .gitkeep:

  1. Runtime directories — Your application expects a logs/ or tmp/ directory to exist at startup. Without it, the app crashes on first run. A .gitkeep ensures the directory is created on clone.
  2. Upload directories — Web frameworks expect an uploads/ folder to exist for handling file uploads.
  3. Project scaffolding — Empty module directories that represent planned structure and will be filled in later.

Combining .gitignore and .gitkeep:

A common and powerful pattern is ignoring a directory's contents while keeping the directory itself:

# .gitignore
logs/*
!logs/.gitkeep

This ignores all log files but preserves the .gitkeep so the logs/ directory exists after a fresh clone.

Alternative: .gitignore inside the directory:

Instead of .gitkeep, you can place a .gitignore file inside the empty directory:

# logs/.gitignore
*
!.gitignore

This ignores all files in the directory (including future logs) while the .gitignore file itself causes git to track the directory. This approach is self-documenting because the ignore rules live exactly where they apply.

Best practice: Use the .gitignore-inside-the-directory approach for directories whose contents should always be ignored (logs, caches, uploads). Use .gitkeep for genuinely empty directories that are part of your project scaffold.

Use Case

A framework scaffolding tool generates empty directories for logs, uploads, and cache that need to exist when developers clone the repo, but their runtime contents should never be tracked.

Try It — .gitignore Generator

Open full tool