Git 配置不同工作空间使用不同的 SSH key 及用户信息
场景
如果在一台设备上同时维护不同的仓库(个人及工作项目),经常会遇到两个问题:
不同工作空间需要使用不同的
user.name和user.email,需要相互隔离。不同工作空间需要使用不同的 SSH key 访问远端仓库。
如果全部写在全局 Git 配置里,很容易出现提交身份串用、推送时用了错误 SSH key 的情况。
可以使用 Git 的 includeIf + ~/.ssh/config,根据仓库所在目录自动加载不同的 gitconfig、SSH key。
思路
整体配置分为四层:
- 全局配置:只放通用配置,以及不同工作空间的
includeIf规则。 - 工作空间配置:分别放对应的
user.name和user.email。 - SSH 配置:在
~/.ssh/config里配置 Host 别名和对应 SSH key。 - 仓库配置:remote 使用 SSH Host 别名,不需要在每个仓库里单独改
user.name、user.email。
本地环境:
- 全局配置里开启
user.useConfigOnly,强制要求每个仓库都必须显式设置用户信息。 - 全局配置里通过
includeIf "gitdir:/Users/<user>/code/personal/"和includeIf "gitdir:/Users/<user>/code/work/"匹配不同工作空间。 - 每个工作空间配置里只管理 Git 用户信息,SSH key 选择交给
~/.ssh/config管理。
参考 Organizing multiple Git identities ,先用目录把不同身份隔离开,再让 Git 根据仓库所在位置自动选择配置。
目录
先按身份或用途划分工作空间,然后把仓库放到对应目录下面。
示例结构:
<code>/
<personal>/
<repo-a>/
<repo-b>/
<work>/
<repo-c>/
<repo-d>/这样做的好处是:
- 克隆仓库时只要在对应目录下,Git 身份就会自动切换。
- 不需要在每个仓库里手动设置
user.name和user.email。 - 不需要为了不同账号修改 remote 地址里的 host 别名。
- 迁移或排查时,只需要看工作空间配置即可。
如果某个子空间下还有更细的分组,也可以继续拆:
<code>/
<personal>/
<group-a>/
<repo-a>/
<group-b>/
<repo-b>/但还是建议维护多个一级工作空间,减少套娃,降低维护时的负担。
全局配置
可以通过编辑全局 Git 配置,加入类似内容:
# /Users/<user>/.gitconfig
# 默认通用配置
[user]
name = zhiyu
email = <default-user-email>
useConfigOnly = true
# personal
[includeIf "gitdir:/Users/<user>/code/personal/"]
path = /Users/<user>/.gitconfig-personal
# work
[includeIf "gitdir:/Users/<user>/code/work/"]
path = /Users/<user>/.gitconfig-work这里有几个点需要注意:
gitdir是工作空间路径。- 结尾建议保留
/,用于匹配该工作空间下的仓库。 path指向对应工作空间的 Git 配置文件,建议平行于默认配置,即根目录下。user.useConfigOnly = true强制要求每个仓库都必须显式设置用户信息。
如果希望没有匹配到工作空间配置时直接报错,可以不在全局配置里写默认的 user.name 和 user.email,只在 personal、work 等工作空间配置里写用户信息。
多层 includeIf
如果有通用工作空间配置,也有更细的身份配置,可以写成多层 includeIf:
# /Users/<user>/.gitconfig
[user]
useConfigOnly = true
# 默认配置
[includeIf "gitdir:/Users/<user>/code/"]
path = /Users/<user>/code/.gitconfig
# personal
[includeIf "gitdir:/Users/<user>/code/personal/"]
path = /Users/<user>/.gitconfig-personal
# work
[includeIf "gitdir:/Users/<user>/code/work/"]
path = /Users/<user>/.gitconfig-work当仓库位于 /Users/<user>/code/personal/ 下时,code 的通用配置和 personal 的工作空间配置都会命中。
如果两个配置文件里写了同一个单值配置,比如 user.email,后覆盖前。所以更通用的规则放前面,更具体的规则放后面。
Git 官方文档里也说明,includeIf 命中的配置内容会被插入到当前配置文件的对应位置;gitdir: 后面的内容会作为匹配 .git 目录位置的模式。
工作空间配置
每个工作空间单独准备一个 Git 配置文件,建议平行于默认配置,即根目录下。
例如个人工作空间:
# /Users/<user>/.gitconfig-personal
[user]
name = <personal-user-name>
email = <personal-user-email>工作项目空间:
# /Users/<user>/.gitconfig-work
[user]
name = <work-user-name>
email = <work-user-email>这样,位于 personal 下的仓库会自动使用个人提交身份;位于 work 下的仓库会自动使用工作提交身份。
SSH key 配置
SSH key 不建议通过 gitconfig: sshCommand 去管理。
个人认为,更解耦的做法是把 SSH key 放到 SSH 自己的配置里,再让 Git remote 使用对应的 Host 别名。
可以在 ~/.ssh/config 中配置:
# /Users/<user>/.ssh/config
Host github
HostName ssh.github.com
User git
# 如果远端服务使用了特殊端口,可以加上 Port
Port 443
IdentityFile /Users/<user>/.ssh/<github-ssh-key>
Host gitlab
HostName <git-server-host>
User git
IdentityFile /Users/<user>/.ssh/<gitlab-ssh-key>其中:
Host:给远端主机起一个本机使用的 SSH 别名,对应 remote URL 里的主机Host部分,不是origin这类 Git remote 名称,详细说明。HostName:真实的远端主机地址。User:Git SSH 通常使用git。IdentityFile:这个 Host 别名要使用的 SSH key。
如果个人和工作仓库都在同一个代码托管平台,也建议配置成两个不同的 Host 别名,让它们分别指向不同 SSH key,以减少提交无权限、越权的可能。
如果本机有很多 SSH key,出现 SSH 自动尝试错误 key 的情况,可以在对应 Host 下加上:
# /Users/<user>/.ssh/config
Host <personal-git-host>
HostName <git-server-host>
User git
IdentityFile /Users/<user>/.ssh/<personal-ssh-key>
IdentitiesOnly yes这样 SSH 会优先使用当前 Host 明确指定的 key。
验证配置是否生效
进入某个仓库后,可以查看当前命中的用户信息:
git config --get user.name
git config --get user.email也可以查看 Git 配置来源:
git config --show-origin --get user.email验证 SSH Host 是否能连通:
ssh -T git@<personal-git-host>
ssh -T git@<work-git-host>如果需要排查 SSH 实际使用了哪个 key,可以加上 -v 查看调试输出:
ssh -vT git@<personal-git-host>remote 配置
需要注意,Git remote 名称和 SSH Host 别名不是同一个东西。
比如:
git remote add github git@github.com:<group>/<repo>.git这里可以拆成三部分看:
github:Git remote 名称,之后可以执行git push github main。git@github.com:<group>/<repo>.git:remote URL。github.com:remote URL 里的 SSHHost,也是 SSH 会去匹配的主机部分。
如果 ~/.ssh/config 里配置的是 Host <personal-git-host>,那么 remote URL 里也要使用这个 Host 别名:
git remote add github git@<personal-git-host>:<group>/<repo>.git示例:
origin git@<personal-git-host>:<group>/<repo>.git (fetch)
origin git@<personal-git-host>:<group>/<repo>.git (push)这里的 <personal-git-host> 要和 ~/.ssh/config 中的 Host <personal-git-host> 保持一致。
remote 里的 Host 别名命中到 ~/.ssh/config 中的配置,Git 执行 fetch、pull、push 时,就会由 SSH 自动选择对应的 key。
常见问题
includeIf 没有生效
优先检查:
仓库是否真的位于对应工作空间下面。
gitdir:/Users/<user>/code/personal/或gitdir:/Users/<user>/code/work/的结尾是否保留了/。path = /Users/<user>/.gitconfig-personal或path = /Users/<user>/.gitconfig-work指向的配置文件是否存在。配置文件里的 ini 语法是否正确。
如果仓库或工作目录已经移动过位置,需要同步更新全局配置里的 includeIf 匹配规则。
如果使用了多层 includeIf,还要注意配置顺序。通用规则建议写在前面,具体身份规则写在后面,且不要让嵌套规则复杂化。
用户信息错误
检查仓库本地配置是否覆盖了工作空间配置:
git config --local --get user.name
git config --local --get user.email如果不想在当前仓库单独覆盖,可以 删除本地配置:
git config --local --unset user.name
git config --local --unset user.email然后重新查看:
git config --get user.name
git config --get user.emailSSH key 错误
先确认当前仓库 remote 里的 Host 别名:
git remote -v再确认 ~/.ssh/config 中是否存在同名 Host 配置。
如果有输出但推送失败,再检查:
- SSH key 是否存在。
- SSH key 是否已经添加到对应代码托管平台。
- remote 地址是否使用 SSH 格式,而不是 HTTPS 格式。
- remote 地址里的 Host 别名是否和
~/.ssh/config中的Host完全一致。
总结
不同工作空间使用不同 Git 身份和 SSH key:
# /Users/<user>/.gitconfig
[includeIf "gitdir:/Users/<user>/code/personal/"]
path = /Users/<user>/.gitconfig-personal再在工作空间配置里写:
# /Users/<user>/.gitconfig-personal
[user]
name = <user-name>
email = <user-email>SSH key 则放到 SSH 配置里:
# /Users/<user>/.ssh/config
Host <personal-git-host>
HostName <git-server-host>
User git
IdentityFile /Users/<user>/.ssh/<personal-ssh-key>这样一来, Git 提交身份由工作空间决定,SSH key 则由 remote 中的 Host 别名决定,两部分互不耦合。
日常进入仓库后正常 git pull、git push 即可。