npx claudepluginhub melodic-software/claude-code-plugins --plugin gitWant just this skill?
Add to a custom plugin, then install with one command.
Multi-identity Git configuration with directory-scoped isolation. Sets up per-directory user email, GPG signing keys, and SSH keys using includeIf conditional includes. Use when configuring work vs personal Git identities, fixing "Unverified" commits from email/GPG key mismatch, setting up multiple GitHub accounts on one machine, auditing identity isolation, or troubleshooting includeIf, GPG key selection, or SSH key routing issues.
This skill is limited to using the following tools:
references/identity-setup-guide.mdreferences/verification-commands.mdGit Multi-Identity Configuration
Directory-scoped Git identity isolation: automatic email, GPG key, and SSH key selection based on repository location.
Table of Contents
- Overview
- When to Use This Skill
- Gather User Details First
- Quick Start
- Setup
- Audit
- Troubleshoot
- Architecture
- Related Skills
- Version History
Overview
Multi-identity isolation solves the problem of using different Git identities (email, GPG key, SSH key) across different contexts on the same machine. Instead of manually switching configuration or setting per-repo overrides, includeIf conditional includes automatically apply the correct identity based on which directory a repository lives in.
What this provides:
- Automatic
user.emailanduser.nameper directory tree - Automatic GPG signing key selection per identity
- Automatic SSH key routing per identity (multiple GitHub accounts)
- Zero manual switching -- commit in any repo and the correct identity applies
When to Use This Skill
- Setting up work vs personal Git identities on the same machine
- Configuring multiple GitHub/GitLab accounts with different SSH keys
- Fixing "Unverified" commits caused by email/GPG key mismatch
- Auditing that identity isolation is working correctly across directories
- Troubleshooting
includeIfnot matching, wrong GPG key used, or SSH "permission denied" - Adding a new identity (new employer, new open-source persona)
Gather User Details First
Before executing any setup commands, use AskUserQuestion to collect the user's specific details. Every identity setup is unique -- do not assume directory paths, email addresses, or identity names.
Required information per identity:
- Identity name (e.g., "work", "personal", "freelance", "open-source")
- Directory path where repos for this identity live (e.g.,
~/Projects/work/) - Git email for this identity
- Git name (if different per identity, or one shared name)
- GPG signing? Whether they want GPG signing (and whether keys already exist)
- SSH key routing? Whether they need separate SSH keys per identity (e.g., multiple GitHub accounts)
- Platform (Windows, macOS, Linux) -- determines
gitdir:vsgitdir/i:syntax
Example AskUserQuestion flow:
- "How many Git identities do you need? What are their names (e.g., work, personal)?"
- "What directory contains your [identity] repositories?"
- "What email address should be used for [identity] commits?"
- "Do you already have GPG keys for each identity, or should we generate them?"
- "Do you use multiple GitHub/GitLab accounts (requiring separate SSH keys)?"
Only proceed with setup commands after collecting these details. Replace all placeholder values in the examples below with the user's actual values.
Quick Start
Minimal end-to-end setup for two identities (work + personal):
# 1. Create directory-scoped gitconfig files
cat > ~/.gitconfig-work << 'EOF'
[user]
email = jane@acme-corp.com
signingkey = <WORK_GPG_KEY_ID>
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
EOF
cat > ~/.gitconfig-personal << 'EOF'
[user]
email = jane@example.com
signingkey = <PERSONAL_GPG_KEY_ID>
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_personal
EOF
# 2. Add includeIf directives to ~/.gitconfig
# (Windows: use gitdir/i: for case-insensitive matching)
git config --global --add includeIf."gitdir/i:C:/Projects/work/".path ~/.gitconfig-work
git config --global --add includeIf."gitdir/i:C:/Projects/personal/".path ~/.gitconfig-personal
# 3. Verify
cd ~/Projects/work/any-repo && git config user.email
# Expected: jane@acme-corp.com
cd ~/Projects/personal/any-repo && git config user.email
# Expected: jane@example.com
For complete step-by-step setup, see references/identity-setup-guide.md.
Setup
Directory Layout Convention
Organize repositories by identity under a common parent:
~/Projects/
work/ # All work repositories
repo-a/
repo-b/
personal/ # All personal repositories
my-project/
dotfiles/
The parent directory (e.g., work/, personal/) is what includeIf gitdir matches against. Ask the user for their actual directory layout -- do not assume paths.
Per-Identity Gitconfig Files
Create a separate gitconfig file for each identity. Each file overrides user.email, user.name (if different), user.signingkey, and optionally core.sshCommand.
Work identity (~/.gitconfig-work):
[user]
email = jane@acme-corp.com
signingkey = ABC123DEF4567890
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
Personal identity (~/.gitconfig-personal):
[user]
email = jane@example.com
signingkey = 1234567890ABCDEF
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_personal
includeIf Directives
Add conditional includes to ~/.gitconfig:
[user]
name = Jane Developer
[commit]
gpgsign = true
# Identity isolation
[includeIf "gitdir/i:C:/Projects/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir/i:C:/Projects/personal/"]
path = ~/.gitconfig-personal
Critical rules:
- Trailing slash required --
gitdir:C:/Projects/work/notgitdir:C:/Projects/work - Windows: use
gitdir/i:-- case-insensitive matching (Windows paths are case-insensitive) - macOS/Linux: use
gitdir:-- case-sensitive matching is fine on case-sensitive filesystems - The
user.namein the main config acts as default; per-identity files only need to override what differs
GPG Key Per Identity
Generate a separate GPG key for each identity email. See git:gpg-signing for detailed key generation.
# Generate work key (use work email)
gpg --full-generate-key
# Email: jane@acme-corp.com
# Generate personal key (use personal email)
gpg --full-generate-key
# Email: jane@example.com
# List keys to get IDs
gpg --list-secret-keys --keyid-format=long
Put the corresponding key ID in each identity's gitconfig file under user.signingkey.
SSH Key Per Identity
Generate a separate SSH key for each identity:
# Work SSH key
ssh-keygen -t ed25519 -C "jane@acme-corp.com" -f ~/.ssh/id_ed25519_work
# Personal SSH key
ssh-keygen -t ed25519 -C "jane@example.com" -f ~/.ssh/id_ed25519_personal
Two routing approaches (pick one):
Option A: core.sshCommand in per-identity gitconfig (recommended -- simpler):
# In ~/.gitconfig-work
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
Option B: ~/.ssh/config Host-based routing (needed for multiple GitHub accounts):
# ~/.ssh/config
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
With Host-based routing, use url.insteadOf in each identity config to transparently rewrite remote URLs:
# In ~/.gitconfig-work
[url "git@github-work:"]
insteadOf = git@github.com:
See references/identity-setup-guide.md for complete SSH routing details.
Adding Keys to GitHub
Each identity's SSH and GPG public keys must be uploaded to the corresponding GitHub account:
- SSH key: Settings > SSH and GPG keys > New SSH key
- GPG key: Settings > SSH and GPG keys > New GPG key (
gpg --armor --export <KEY_ID>) - Verify email: The email on the GPG key must be a verified email on the GitHub account
Audit
Verify identity isolation is working correctly across all directories.
Check Effective Identity
# Check effective identity in any directory
cd /path/to/repo
git config user.email
git config user.name
git config user.signingkey
git config core.sshCommand
# Show where each value comes from
git config --show-origin user.email
git config --show-origin user.signingkey
Detect Mismatches
# Compare Git email vs GPG key email
GIT_EMAIL=$(git config user.email)
KEY_ID=$(git config user.signingkey)
GPG_EMAIL=$(gpg --list-keys "$KEY_ID" 2>/dev/null | grep -oP '<\K[^>]+')
if [ "$GIT_EMAIL" != "$GPG_EMAIL" ]; then
echo "MISMATCH: Git=$GIT_EMAIL GPG=$GPG_EMAIL"
else
echo "OK: $GIT_EMAIL"
fi
Audit All Identity Directories
Ask the user which directories to audit, then check each:
# Check identity directories (replace with user's actual paths)
for dir in ~/Projects/work ~/Projects/personal; do
echo "=== $dir ==="
git -C "$dir/$(ls "$dir" | head -1)" config user.email
git -C "$dir/$(ls "$dir" | head -1)" config user.signingkey
done
See references/verification-commands.md for comprehensive verification scripts.
Troubleshoot
"Unverified" Commits on GitHub
Cause: The email in the commit signature does not match a verified email on the GitHub account, or the GPG public key is not uploaded.
Diagnosis:
# Check what email was used in the commit
git log --format='%ae %GK %G?' -1
# %ae = author email, %GK = signing key, %G? = signature status
# G = good, B = bad, N = no signature, U = untrusted
Fixes:
- Upload GPG public key to GitHub (Settings > SSH and GPG keys)
- Ensure Git email matches GPG key email matches GitHub verified email
- Re-sign past commits if needed:
git rebase --exec 'git commit --amend --no-edit -S' HEAD~N
includeIf Not Matching
Symptoms: git config user.email shows global default instead of per-directory value.
Common causes:
- Missing trailing slash:
gitdir:C:/Projects/workmust begitdir:C:/Projects/work/ - Case sensitivity on Windows: Use
gitdir/i:instead ofgitdir: - Path format mismatch: Use forward slashes on all platforms; Git normalizes internally
- Not inside a git repo:
includeIf gitdironly activates inside a git repository
Diagnosis:
# Show all config with origins -- look for the includeIf file
git config --list --show-origin | grep -i "user\."
# Verify the includeIf directive
git config --global --list | grep includeIf
Wrong GPG Key Used
Symptoms: Commits signed with wrong key; "Unverified" despite key being uploaded.
Diagnosis:
# Check which key Git is using in this directory
git config user.signingkey
# Check which includeIf file is providing it
git config --show-origin user.signingkey
# Verify the key exists
gpg --list-secret-keys --keyid-format=long $(git config user.signingkey)
SSH Permission Denied (Wrong Key Offered)
Symptoms: git push fails with "Permission denied (publickey)" despite having SSH keys.
Diagnosis:
# Check which SSH command Git is using
git config core.sshCommand
# Test SSH connection with verbose output
ssh -vT git@github.com 2>&1 | grep "Offering"
# If using Host-based routing, test the specific host alias
ssh -vT git@github-work 2>&1 | grep "Offering"
Fixes:
- Verify
core.sshCommandpoints to correct key file - Verify
~/.ssh/confighasIdentitiesOnly yesto prevent SSH agent from offering wrong keys - Verify the SSH key is added to the correct GitHub account
Architecture
How includeIf Works
Git's includeIf evaluates conditions when reading configuration. For gitdir:
- Git determines the current repository's
.gitdirectory path - It normalizes the path (resolves symlinks, uses forward slashes)
- It checks if the normalized path starts with the
gitdirpattern - If matched, the referenced file is included and its settings override earlier values
Config precedence with includeIf:
System config (/etc/gitconfig)
-> Global config (~/.gitconfig)
-> includeIf matched files (in order they appear)
-> Local config (.git/config)
Local config (.git/config) still has highest priority. An includeIf file sits between global and local.
Multiple includeIf Files
If multiple includeIf directives match, they are all included in order. Later includes override earlier ones for the same settings.
gitdir vs gitdir/i
| Variant | Case Sensitivity | Use On |
|---|---|---|
gitdir: | Case-sensitive | macOS (APFS), Linux (ext4) |
gitdir/i: | Case-insensitive | Windows (NTFS), macOS (HFS+) |
Always use gitdir/i: on Windows. On macOS, use gitdir/i: unless you know the filesystem is case-sensitive.
Related Skills
- gpg-signing: GPG key generation, passphrase caching, platform setup, and troubleshooting
- git-config: Configuration hierarchy, aliases, credentials, and performance tuning
- setup: Git installation, initial configuration, and platform-specific setup
Version History
- v1.0.0 (2026-02-16): Initial release -- multi-identity isolation with includeIf, GPG, SSH, audit, and troubleshooting
Last Updated
Date: 2026-02-16 Model: claude-opus-4-6