名称: git-identity 描述: 基于目录范围隔离的Git多身份配置。通过includeIf条件包含设置每目录用户邮箱、GPG签名密钥和SSH密钥。适用于配置工作与个人Git身份、修复因邮箱/GPG密钥不匹配导致的“未验证”提交、在一台机器上设置多个GitHub账户、审计身份隔离效果或排查includeIf、GPG密钥选择或SSH密钥路由问题。 允许工具: 读取, Bash, Glob, Grep
Git多身份配置
目录范围的Git身份隔离:基于仓库位置自动选择邮箱、GPG密钥和SSH密钥。
目录
概述
多身份隔离解决了在同一台机器上跨不同上下文使用不同Git身份(邮箱、GPG密钥、SSH密钥)的问题。通过includeIf条件包含,基于仓库所在目录自动应用正确的身份设置,无需手动切换配置或设置每个仓库的覆盖。
此技能提供:
- 每个目录树的自动
user.email和user.name设置 - 每个身份的自动GPG签名密钥选择
- 每个身份的自动SSH密钥路由(多个GitHub账户)
- 零手动切换——在任何仓库提交时自动应用正确身份
何时使用此技能
- 在同一台机器上设置工作与个人Git身份
- 配置具有不同SSH密钥的多个GitHub/GitLab账户
- 修复因邮箱/GPG密钥不匹配导致的“未验证”提交
- 审计跨目录的身份隔离是否正常工作
- 排查
includeIf不匹配、使用错误GPG密钥或SSH“权限被拒绝”问题 - 添加新身份(如新雇主、新开源身份)
首先收集用户详情
在执行任何设置命令之前,使用AskUserQuestion收集用户的具体详情。每个身份设置都是唯一的——不要假设目录路径、邮箱地址或身份名称。
每个身份所需信息:
- 身份名称(例如,“工作”、“个人”、“自由职业”、“开源”)
- 目录路径,该身份仓库所在位置(例如,
~/Projects/work/) - Git邮箱用于此身份
- Git名称(如果每个身份不同,或共享一个名称)
- **GPG签名?**是否想要GPG签名(以及密钥是否已存在)
- **SSH密钥路由?**是否需要每个身份单独的SSH密钥(例如,多个GitHub账户)
- 平台(Windows、macOS、Linux)——决定
gitdir:与gitdir/i:语法
示例AskUserQuestion流程:
- “您需要多少个Git身份?它们的名称是什么(例如,工作、个人)?”
- “哪个目录包含您的[身份]仓库?”
- “哪个邮箱地址应用于[身份]提交?”
- “您是否已经拥有每个身份的GPG密钥,或者我们应该生成它们?”
- “您是否使用多个GitHub/GitLab账户(需要单独的SSH密钥)?”
收集这些详情后才继续设置命令。替换下面示例中的所有占位值为用户的实际值。
快速开始
两个身份(工作 + 个人)的最小端到端设置:
# 1. 创建目录范围的gitconfig文件
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. 添加includeIf指令到~/.gitconfig
# (Windows:使用gitdir/i:进行不区分大小写匹配)
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. 验证
cd ~/Projects/work/any-repo && git config user.email
# 预期: jane@acme-corp.com
cd ~/Projects/personal/any-repo && git config user.email
# 预期: jane@example.com
完整逐步设置,参见references/identity-setup-guide.md。
设置
目录布局约定
按身份在公共父目录下组织仓库:
~/Projects/
work/ # 所有工作仓库
repo-a/
repo-b/
personal/ # 所有个人仓库
my-project/
dotfiles/
父目录(例如,work/、personal/)是includeIf gitdir匹配的对象。询问用户的实际目录布局——不要假设路径。
每个身份的Gitconfig文件
为每个身份创建单独的gitconfig文件。每个文件覆盖user.email、user.name(如果不同)、user.signingkey,并可选的core.sshCommand。
工作身份 (~/.gitconfig-work):
[user]
email = jane@acme-corp.com
signingkey = ABC123DEF4567890
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
个人身份 (~/.gitconfig-personal):
[user]
email = jane@example.com
signingkey = 1234567890ABCDEF
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_personal
includeIf指令
添加条件包含到~/.gitconfig:
[user]
name = Jane Developer
[commit]
gpgsign = true
# 身份隔离
[includeIf "gitdir/i:C:/Projects/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir/i:C:/Projects/personal/"]
path = ~/.gitconfig-personal
关键规则:
- 必须包含尾随斜杠——
gitdir:C:/Projects/work/而不是gitdir:C:/Projects/work - Windows:使用
gitdir/i:——不区分大小写匹配(Windows路径不区分大小写) - macOS/Linux:使用
gitdir:——在区分大小写的文件系统上,区分大小写匹配即可 - 主配置中的
user.name作为默认值;每个身份文件仅需覆盖不同部分
每个身份的GPG密钥
为每个身份邮箱生成单独的GPG密钥。参见git:gpg-signing获取详细密钥生成指南。
# 生成工作密钥(使用工作邮箱)
gpg --full-generate-key
# 邮箱: jane@acme-corp.com
# 生成个人密钥(使用个人邮箱)
gpg --full-generate-key
# 邮箱: jane@example.com
# 列出密钥以获取ID
gpg --list-secret-keys --keyid-format=long
将相应密钥ID放入每个身份的gitconfig文件的user.signingkey下。
每个身份的SSH密钥
为每个身份生成单独的SSH密钥:
# 工作SSH密钥
ssh-keygen -t ed25519 -C "jane@acme-corp.com" -f ~/.ssh/id_ed25519_work
# 个人SSH密钥
ssh-keygen -t ed25519 -C "jane@example.com" -f ~/.ssh/id_ed25519_personal
两种路由方法(选择一种):
选项A:在身份gitconfig中使用core.sshCommand(推荐——更简单):
# 在~/.gitconfig-work中
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
选项B:基于主机的~/.ssh/config路由(需要多个GitHub账户):
# ~/.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
使用基于主机的路由时,在每个身份配置中使用url.insteadOf透明重写远程URL:
# 在~/.gitconfig-work中
[url "git@github-work:"]
insteadOf = git@github.com:
参见references/identity-setup-guide.md获取完整SSH路由详情。
添加密钥到GitHub
每个身份的SSH和GPG公钥必须上传到相应的GitHub账户:
- SSH密钥:设置 > SSH和GPG密钥 > 新SSH密钥
- GPG密钥:设置 > SSH和GPG密钥 > 新GPG密钥(
gpg --armor --export <KEY_ID>) - 验证邮箱:GPG密钥上的邮箱必须是GitHub账户上的已验证邮箱
审计
验证身份隔离在所有目录中正常工作。
检查有效身份
# 在任何目录检查有效身份
cd /path/to/repo
git config user.email
git config user.name
git config user.signingkey
git config core.sshCommand
# 显示每个值的来源
git config --show-origin user.email
git config --show-origin user.signingkey
检测不匹配
# 比较Git邮箱与GPG密钥邮箱
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 "不匹配: Git=$GIT_EMAIL GPG=$GPG_EMAIL"
else
echo "OK: $GIT_EMAIL"
fi
审计所有身份目录
询问用户要审计的目录,然后检查每个:
# 检查身份目录(替换为用户的实际路径)
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
参见references/verification-commands.md获取全面验证脚本。
故障排查
GitHub上“未验证”提交
原因: 提交签名中的邮箱与GitHub账户上的已验证邮箱不匹配,或GPG公钥未上传。
诊断:
# 检查提交中使用的邮箱
git log --format='%ae %GK %G?' -1
# %ae = 作者邮箱, %GK = 签名密钥, %G? = 签名状态
# G = 好, B = 坏, N = 无签名, U = 不信任
修复:
- 上传GPG公钥到GitHub(设置 > SSH和GPG密钥)
- 确保Git邮箱匹配GPG密钥邮箱匹配GitHub已验证邮箱
- 如有需要,重新签名过去提交:
git rebase --exec 'git commit --amend --no-edit -S' HEAD~N
includeIf不匹配
症状: git config user.email显示全局默认值而不是每个目录值。
常见原因:
- 缺少尾随斜杠:
gitdir:C:/Projects/work必须为gitdir:C:/Projects/work/ - Windows上的区分大小写:使用
gitdir/i:而不是gitdir: - 路径格式不匹配:在所有平台使用正斜杠;Git内部规范化
- 不在git仓库内:
includeIf gitdir仅在git仓库内激活
诊断:
# 显示所有配置及其来源——查找includeIf文件
git config --list --show-origin | grep -i "user\."
# 验证includeIf指令
git config --global --list | grep includeIf
使用错误GPG密钥
症状: 提交使用错误密钥签名;尽管密钥已上传,仍“未验证”。
诊断:
# 检查在此目录中Git使用哪个密钥
git config user.signingkey
# 检查哪个includeIf文件提供它
git config --show-origin user.signingkey
# 验证密钥是否存在
gpg --list-secret-keys --keyid-format=long $(git config user.signingkey)
SSH权限被拒绝(提供错误密钥)
症状: 尽管有SSH密钥,git push失败并显示“权限被拒绝(公钥)”。
诊断:
# 检查Git使用哪个SSH命令
git config core.sshCommand
# 使用详细输出测试SSH连接
ssh -vT git@github.com 2>&1 | grep "Offering"
# 如果使用基于主机的路由,测试特定主机别名
ssh -vT git@github-work 2>&1 | grep "Offering"
修复:
- 验证
core.sshCommand指向正确密钥文件 - 验证
~/.ssh/config有IdentitiesOnly yes以防止SSH代理提供错误密钥 - 验证SSH密钥已添加到正确的GitHub账户
架构
includeIf如何工作
Git的includeIf在读取配置时评估条件。对于gitdir:
- Git确定当前仓库的
.git目录路径 - 它规范化路径(解析符号链接,使用正斜杠)
- 检查规范化路径是否以
gitdir模式开头 - 如果匹配,则包含引用文件,其设置覆盖先前值
includeIf的配置优先级:
系统配置 (/etc/gitconfig)
-> 全局配置 (~/.gitconfig)
-> includeIf匹配文件(按出现顺序)
-> 本地配置 (.git/config)
本地配置(.git/config)仍有最高优先级。includeIf文件位于全局和本地之间。
多个includeIf文件
如果多个includeIf指令匹配,它们都按顺序包含。对于相同设置,后来者覆盖先前者。
gitdir vs gitdir/i
| 变体 | 区分大小写 | 使用于 |
|---|---|---|
gitdir: |
区分大小写 | macOS(APFS)、Linux(ext4) |
gitdir/i: |
不区分大小写 | Windows(NTFS)、macOS(HFS+) |
Windows上始终使用gitdir/i:。macOS上,除非知道文件系统区分大小写,否则使用gitdir/i:。
相关技能
- gpg-signing:GPG密钥生成、口令缓存、平台设置和故障排查
- git-config:配置层次、别名、凭证和性能调优
- setup:Git安装、初始配置和平台特定设置
版本历史
- v1.0.0 (2026-02-16):初始发布——包含includeIf、GPG、SSH、审计和故障排查的多身份隔离
最后更新
日期: 2026-02-16 模型: claude-opus-4-6