Git's Magic Files
- title
- Git's Magic Files
- type
- summary
- summary
- Committed files that control git behavior โ .gitignore, .gitattributes, .mailmap, .git-blame-ignore-revs
- tags
- git, developer-tools
- sources
- git-magic-files
- created
- 2026-04-07
- updated
- 2026-04-22
Andrew Nesbitt catalogs the committed files that control how git behaves for everyone who clones a repository. Unlike .git/ configuration, these files travel with the code and take effect automatically.
The core seven
.gitignore is the one everyone knows โ pattern-based exclusion of untracked files. Git evaluates ignore rules from multiple locations: per-directory .gitignore files, .git/info/exclude for local-only patterns, and a global ignore file. The ** glob matches nested directories. One common gotcha: .gitignore only affects untracked files. If a file is already tracked, adding it to .gitignore does nothing until you git rm --cached it.
.gitattributes does far more than most teams realize. It configures LFS filters (*.psd filter=lfs diff=lfs merge=lfs), line ending normalization (*.sh text eol=lf), merge strategies (package-lock.json merge=ours to auto-resolve lockfile conflicts), and GitHub Linguist overrides (vendor/* linguist-vendored to exclude vendored code from language stats). Marking files as binary prevents git from attempting diffs or three-way merges on them.
.mailmap maps messy author identities to canonical ones. If someone committed under three different email addresses, mailmap collapses them into one for git log, shortlog, and blame. GitHub's contributor graphs ignore mailmap, though, so duplicates persist in the web UI.
.git-blame-ignore-revs lists commits that git blame should skip โ the classic use case is a bulk formatting run that touches every file but changes no logic. GitHub, GitLab (since 15.4), and Gitea read this file automatically. Locally you need git config blame.ignoreRevsFile .git-blame-ignore-revs.
.gitmodules is created automatically by git submodule add and defines where submodule repos live and which branch to track.
.lfsconfig stores Git LFS endpoint URLs and transfer settings so LFS works without per-clone configuration.
.gitmessage is a commit message template, but unlike the others it requires manual setup (git config commit.template .gitmessage) per clone โ it won't activate just by being in the repo.
Forge-specific folders
Each git forge has its own config directory for CI workflows, issue templates, and CODEOWNERS: .github/, .gitlab/, .gitea/, .forgejo/, .bitbucket/. The interesting bit is the fallback chains โ Forgejo checks .forgejo/ then .gitea/ then .github/, and Gitea checks .gitea/ then .github/. SourceHut skips the folder convention entirely and uses .build.yml at the root.
Minor conventions
.gitkeep is a community convention (not a git feature) to force-track empty directories. .gitconfig files in repos are suggested configs that developers manually include โ git won't auto-load them for security reasons. .gitsigners tracks trusted GPG/SSH signing keys. .gitreview configures Gerrit integration. .gitlint configures commit message linting.
Nesbitt also notes that Jujutsu (.jj/) coexists with .git/ and respects all of these magic files.
Advice for tool authors
If you're building tools that traverse git repos, respect the magic files: read .gitignore when walking the tree, check .gitattributes to identify binary/vendored/generated files, use .mailmap for author info, and handle .gitmodules for submodules. Git's config format ([section "subsection"] key = value) has parsers available in most languages through their git libraries.
Related conventions outside git
.editorconfig standardizes indentation and line endings across editors. Language version files (.ruby-version, .node-version, .python-version, .tool-versions) tell version managers which runtime to use. .dockerignore uses the same syntax as .gitignore but applies to Docker build contexts.
For a different angle on git internals โ querying the full commit/blob/path history with SQL rather than config files โ see linux-kernel-pgit and pgit. For tooling that rewrites that history safely rather than reading it, see git-history-command.