.gitignore and Git submodules: Excluding files and embedding projects
In short: The .gitignore file defines what Git does not version; submodules embed another repository at a fixed state into your own.
In more detail: Not everything in a project folder belongs in version control: generated files, dependencies, secrets. And sometimes you need foreign or shared code without copying it.
In Depth
.gitignore
.gitignore contains patterns for files that Git should skip:
node_modules/
dist/
*.log
.env
.DS_Store
!important.log # exception with !
/build # only in the main folder
Comments begin with #. A .gitignore only applies to not yet tracked files. If something was already checked in, it stays tracked until you remove it from the index with git rm --cached file. Folder-specific files can live in subfolders; global rules live in ~/.config/git/ignore. Templates for many languages are offered by gitignore.io and the repository github/gitignore.
Secrets
Passwords, API keys and .env files never belong in the repository. Once checked in, a secret stays in the history and must be considered compromised: revoke and renew keys immediately; removing it from history (git filter-repo) is laborious. Better put an .env.example without values.
Submodules
A submodule is a reference to a specific commit of another repository:
git submodule add https://github.com/company/library libs/library
git clone --recurse-submodules <url> # clone with submodules
git submodule update --init --recursive # load afterwards
git submodule update --remote # bump to a newer state
The file .gitmodules holds path and address. The main project remembers the exact commit, so everyone uses the same state.
When submodules, when alternatives
Submodules are usable but error-prone: you forget to update them or push changes only in the main project. Often package managers (npm, pip, Maven) or a monorepo are simpler. A variant is subtrees (git subtree), which copy the code instead of referencing it.
See also: Git, Environment variables, Monorepo, API keys