From git
Configures per-directory Git identities for user.email, GPG signing keys, and SSH keys using includeIf conditionals. Use for work/personal setups, multiple GitHub accounts, fixing unverified commits, auditing isolation, or troubleshooting includeIf/GPG/SSH issues.
npx claudepluginhub melodic-software/claude-code-plugins --plugin gitThis skill is limited to using the following tools:
Directory-scoped Git identity isolation: automatic email, GPG key, and SSH key selection based on repository location.
Manages Git identities and GPG signing profiles per repository: discovers from GPG/git config, lists profiles, switches configs, removes, diagnoses signing health via doctor/list/use/verify.
Sets up, configures, and troubleshoots GPG commit signing in Git across Windows (Gpg4win), macOS (GPG Suite), Linux (gnupg), and WSL. Covers key generation, passphrase caching, GitHub integration, and common errors.
Enforces Git security best practices for 2025 including signed commits, zero-trust workflows, secret scanning, verification, audit logging, and branch protection. Useful for securing repositories and CI/CD pipelines.
Share bugs, ideas, or general feedback.
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 = 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.
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 = 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
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: 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.
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.
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 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:
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