plugins/git/skills/git-identity/SKILL.md
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.
npx skillsauth add melodic-software/claude-code-plugins git-identityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Directory-scoped Git identity isolation: automatic email, GPG key, and SSH key selection based on repository location.
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:
user.email and user.name per directory treeincludeIf not matching, wrong GPG key used, or SSH "permission denied"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:
~/Projects/work/)gitdir: vs gitdir/i: syntaxExample AskUserQuestion flow:
Only proceed with setup commands after collecting these details. Replace all placeholder values in the examples below with the user's actual values.
Minimal end-to-end setup for two identities (work + personal):
# 1. Create directory-scoped gitconfig files
cat > ~/.gitconfig-work << 'EOF'
[user]
email = [email protected]
signingkey = <WORK_GPG_KEY_ID>
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
EOF
cat > ~/.gitconfig-personal << 'EOF'
[user]
email = [email protected]
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: [email protected]
cd ~/Projects/personal/any-repo && git config user.email
# Expected: [email protected]
For complete step-by-step setup, see references/identity-setup-guide.md.
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.
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 = [email protected]
signingkey = ABC123DEF4567890
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
Personal identity (~/.gitconfig-personal):
[user]
email = [email protected]
signingkey = 1234567890ABCDEF
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_personal
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:
gitdir:C:/Projects/work/ not gitdir:C:/Projects/workgitdir/i: -- case-insensitive matching (Windows paths are case-insensitive)gitdir: -- case-sensitive matching is fine on case-sensitive filesystemsuser.name in the main config acts as default; per-identity files only need to override what differsGenerate 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: [email protected]
# Generate personal key (use personal email)
gpg --full-generate-key
# Email: [email protected]
# 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.
Generate a separate SSH key for each identity:
# Work SSH key
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519_work
# Personal SSH key
ssh-keygen -t ed25519 -C "[email protected]" -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 = [email protected]:
See references/identity-setup-guide.md for complete SSH routing details.
Each identity's SSH and GPG public keys must be uploaded to the corresponding GitHub account:
gpg --armor --export <KEY_ID>)Verify identity isolation is working correctly across all directories.
# 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
# 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
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.
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:
git rebase --exec 'git commit --amend --no-edit -S' HEAD~NSymptoms: git config user.email shows global default instead of per-directory value.
Common causes:
gitdir:C:/Projects/work must be gitdir:C:/Projects/work/gitdir/i: instead of gitdir:includeIf gitdir only activates inside a git repositoryDiagnosis:
# 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
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)
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 [email protected] 2>&1 | grep "Offering"
# If using Host-based routing, test the specific host alias
ssh -vT git@github-work 2>&1 | grep "Offering"
Fixes:
core.sshCommand points to correct key file~/.ssh/config has IdentitiesOnly yes to prevent SSH agent from offering wrong keysGit's includeIf evaluates conditions when reading configuration. For gitdir:
.git directory pathgitdir patternConfig 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.
If multiple includeIf directives match, they are all included in order. Later includes override earlier ones for the same settings.
| 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.
Date: 2026-02-16 Model: claude-opus-4-6
development
Search Milan Jovanovic's .NET blog for Clean Architecture, DDD, CQRS, EF Core, and ASP.NET Core patterns. Use for finding applicable patterns, code examples, and architecture guidance. Invoke when working with .NET projects that could benefit from proven architectural patterns.
tools
Install and configure Data API Builder (DAB) for production SQL Server MCP access with RBAC
tools
Manage MssqlMcp servers - status, rebuild, and upstream updates
tools
Developer environment setup guides for Windows, macOS, Linux, and WSL. Use when setting up development machines, installing tools, configuring environments, or following platform-specific setup guides. Covers package management, shell/terminal, code editors, AI tooling, containerization, databases, and more.