If you’re like most developers, you probably work on multiple projects across different contexts. Maybe you contribute to open source projects with your personal email, work on company projects with your work email, and maintain client projects with yet another identity. Manually switching git configurations between projects is tedious and error-prone. Fortunately, Git has a powerful feature that solves this problem elegantly: conditional includes with includeIf.
The Problem
Consider this common scenario: You’ve just finished committing some personal project code, then switch to your work repository and make a commit. Hours later, you realize with horror that your personal email is now in your company’s git history. This can happen especially as development setups and projects increase in complexity over time. On-call all nighters don’t help either and it’s an easy mistake to make.
The traditional solution involves manually running git config user.email every time you switch contexts, but this is fragile and easy to forget.
The Solution: Conditional Includes
Git’s includeIf directive allows you to automatically load different configuration files based on the repository’s location. This means you can set up your git config once and never worry about it again.
How It Works
The basic syntax in your global ~/.gitconfig file looks like this:
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir:~/personal/"]
path = ~/.gitconfig-personal
When you run a git command, Git checks the current repository’s location against these patterns. If there’s a match, it loads the additional configuration file specified in the path directive.
Setting It Up
Let’s walk through a complete setup for managing work and personal projects.
Step 1: Organize Your Repositories
First, organize your repositories by context. For example:
~/work/ # All work-related repositories
~/personal/ # Personal projects
~/clients/ # Client projects
Step 2: Create Separate Config Files
Create a git configuration file for each context. For work:
# ~/.gitconfig-work
[user]
name = Your Name
email = you@company.com
signingkey = WORK_GPG_KEY_ID
[commit]
gpgsign = true
For personal projects:
# ~/.gitconfig-personal
[user]
name = Your Name
email = you@personal.com
signingkey = PERSONAL_GPG_KEY_ID
[commit]
gpgsign = true
Step 3: Update Your Global Config
Edit your ~/.gitconfig to include conditional directives:
[user]
# Fallback configuration
name = Your Name
email = you@personal.com
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir:~/personal/"]
path = ~/.gitconfig-personal
[includeIf "gitdir:~/clients/"]
path = ~/.gitconfig-clients
Important Notes About Pattern Matching
Trailing Slashes Matter
The gitdir pattern must end with a forward slash to match a directory:
# Correct - matches ~/work/ and all subdirectories
[includeIf "gitdir:~/work/"]
# Wrong - won't match subdirectories properly
[includeIf "gitdir:~/work"]
Case Sensitivity
On case-sensitive filesystems (Linux, macOS with case-sensitive APFS), the paths are case-sensitive. On Windows and standard macOS, they’re case-insensitive.
Wildcards
You can use ** for more complex matching patterns:
# Match any "company-name" directory anywhere
[includeIf "gitdir:**/company-name/**"]
path = ~/.gitconfig-company
Alternative Approach: Matching by Remote URL
If you don’t organize your repositories by directory, or if you work with multiple Git hosting services, you can use hasconfig:remote.*.url to apply configurations based on the remote URL pattern. This is particularly useful when you use GitHub for personal projects, GitLab for work, and Codeberg for open source contributions.
Setup by Remote URL
# ~/.gitconfig
[includeIf "hasconfig:remote.*.url:git@github.com:your-work-org/**"]
path = ~/.gitconfig-work
[includeIf "hasconfig:remote.*.url:git@gitlab.com:company/**"]
path = ~/.gitconfig-company
[includeIf "hasconfig:remote.*.url:https://codeberg.org/**"]
path = ~/.gitconfig-codeberg
[includeIf "hasconfig:remote.*.url:git@bitbucket.org:*/**"]
path = ~/.gitconfig-bitbucket
[includeIf "hasconfig:remote.*.url:git@github.com:your-personal/**"]
path = ~/.gitconfig-personal
Why This Is Useful
This approach has several advantages:
- Flexible organization: Your repos can live anywhere on your filesystem
- Service-specific configs: Apply different settings based on GitHub vs GitLab vs Codeberg
- Organization-based: Match specific organizations or groups within a hosting service
- Protocol-agnostic: Works with both SSH and HTTPS URLs
Combining Both Approaches
You can use both gitdir and hasconfig:remote.*.url together for maximum flexibility:
[user]
name = Your Name
email = personal@example.com
# Directory-based rules (checked first)
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
# Remote URL-based rules (useful for exceptions)
[includeIf "hasconfig:remote.*.url:git@github.com:opensource-project/**"]
path = ~/.gitconfig-opensource
Note: The hasconfig:remote.*.url condition requires that the repository already has a remote configured. It won’t work immediately after git init but will activate once you add a remote with git remote add.
Advanced Use Cases
Different SSH Keys
You can configure different SSH keys for different contexts by including SSH configuration in your conditional config files:
# ~/.gitconfig-work
[user]
email = you@company.com
[core]
sshCommand = ssh -i ~/.ssh/id_rsa_work
URL Rewrites
Automatically use different protocols or paths:
# ~/.gitconfig-work
[url "git@github.com-work:"]
insteadOf = git@github.com:
Different Default Branches
Set different default branch names per context:
# ~/.gitconfig-personal
[init]
defaultBranch = main
# ~/.gitconfig-work
[init]
defaultBranch = master
Pro Tip: Editing Conditional Config Files Directly
Instead of manually opening your conditional config files in an editor, you can use git’s --file flag to edit them directly:
# Edit your work config
git config --file=~/.gitconfig-work user.email "newemail@company.com"
# Add a new setting to your personal config
git config --file=~/.gitconfig-personal core.editor "vim"
# List all settings in a specific config file
git config --file=~/.gitconfig-work --list
This is especially handy when you can’t remember the exact path to your config files or want to quickly update a setting without opening an editor.
Verifying Your Configuration
To check which configuration is being used in a repository, run:
git config --list --show-origin
This shows each configuration value and which file it comes from. You should see values from your conditional config file for repositories in the matching directories.
To test a specific value:
git config --get user.email
Troubleshooting
If your conditional configuration isn’t working:
- Check for typos in the
gitdirpath, especially the trailing slash - Use absolute paths or
~for home directory, not relative paths - Verify file permissions on your config files
- Check the order - later includes override earlier ones
- Remember that
includeIfonly works in the global config file, not in repository-local configs
Why This Matters
Beyond just getting the right email in commits, conditional configurations enable:
- Security: Use different GPG keys for signing commits in different contexts
- Compliance: Ensure company policies are followed automatically in work repositories
- Productivity: Eliminate context-switching friction and mental overhead
- Reliability: Prevent embarrassing mistakes like using personal credentials in company code
Further Reading
For more details on conditional includes and all available options, check out the official Git documentation on conditional includes. The docs cover additional conditions like onbranch that can provide even more granular control.
Conclusion
Git’s includeIf feature is a simple but powerful tool that saves time and prevents mistakes. By spending a few minutes setting up conditional configurations, you can work seamlessly across different projects and contexts without ever thinking about your git configuration again.
The next time you clone a new repository, it will automatically pick up the right configuration based on where you put it. That’s the kind of automation that makes development just a little bit smoother.