# Git IncludeIf 管理多个 Git 身份


同时维护工作项目和个人项目时，Git 身份很容易用错：公司项目里用了个人邮箱，开源项目里暴露了工作邮箱。手动切换 Git 配置很麻烦，也容易出错。

Git 的 **includeIf** 可以按条件自动加载不同配置文件，用来管理多套身份很合适。

## 为什么需要 includeIf？

在日常开发中，我们常常需要在不同的身份之间切换：

- **工作项目**：使用公司邮箱（`work@example.com`）、公司的 GPG 签名密钥、公司的 SSH 密钥
- **个人项目**：使用私人邮箱（`personal@example.com`）、个人的 GPG 密钥、个人的 SSH 密钥
- **开源贡献**：可能又是另一套配置

如果没有 includeIf，你需要：

1. 每次切换项目时记得运行 `git config user.email xxx@xxx.com`
2. 或者在每个仓库中手动设置本地配置
3. 提交前反复检查当前使用的是否是正确的身份

这种手动方式很麻烦，也**极易出错**。一旦用错身份提交并推送到远程仓库，修改起来会很麻烦。

## includeIf 是什么？

`includeIf` 是 Git 2.13（2017 年 5 月）引入的条件配置包含功能。它允许你根据特定条件（如仓库路径、远程 URL、当前分支等）自动加载不同的配置文件。

基本语法如下：

```ini
[includeIf "条件"]
    path = 要包含的配置文件路径
```

当 Git 读取配置时，会评估条件。如果条件满足，指定路径的配置文件内容就会被包含进来，其中的配置会**覆盖**之前的设置。

## includeIf 的几种常用条件

### I. 基于目录路径（gitdir）

这是最常用的方式，根据仓库所在的目录来决定使用哪个配置：

```ini
[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work

[includeIf "gitdir:~/personal/"]
    path = ~/.gitconfig-personal
```

**注意事项**：

- 路径末尾的 `/` 很重要！`~/work/` 会匹配该目录下的所有子目录
- 如果写成 `~/work`（没有斜杠），只会精确匹配这个路径
- 波浪号 `~` 会自动展开为用户主目录

### II. 大小写不敏感的路径匹配（gitdir/i）

在 Windows 系统上特别有用：

```ini
[includeIf "gitdir/i:C:/Work/"]
    path = C:/Users/username/.gitconfig-work
```

### III. 基于分支名称（onbranch）

根据当前所在的分支来加载配置：

```ini
[includeIf "onbranch:main"]
    path = ~/.gitconfig-main
```

### IV. 基于远程 URL（hasconfig:remote.\*.url）⭐

这是 Git 2.36（2022 年 4 月）引入的强大功能，可以根据仓库的远程 URL 来决定配置。这也是本文重点介绍的方式。

```ini
[includeIf "hasconfig:remote.*.url:https://github.com/**"]
    path = ~/.gitconfig-github

[includeIf "hasconfig:remote.*.url:https://gitlab.company.com/**"]
    path = ~/.gitconfig-work
```

## 实战：使用 hasconfig:remote.\*.url 区分身份

现在让我们看一个完整的配置示例，展示如何优雅地管理工作和个人两种身份。

### 主配置文件 ~/.gitconfig

```ini
[core]
    autocrlf = input
[commit]
    gpgsign = true
[gpg]
    format = ssh

[includeIf "hasconfig:remote.*.url:work:<name>/**"]
    path = /home/nite/.ssh/work.gitconfig

[includeIf "hasconfig:remote.*.url:personal:<name>/**"]
    path = /home/nite/.ssh/personal.gitconfig
```

这里的 `work:<name>` 和 `personal:<name>` 是使用了 Git 的 URL 别名功能。

### 工作配置 ~/.gitconfig-work

```ini
[user]
    name = "Your Work Name"
    email = "work@example.com"
    signingKey = "~/.ssh/id_work.pub"

[commit]
    gpgsign = true

[core]
    sshCommand = "ssh -i ~/.ssh/id_work -o IdentitiesOnly=yes"
```

这个配置文件会在你操作公司 GitLab 仓库时自动生效，包含：

- 工作邮箱
- 工作用的 SSH 签名密钥
- 强制 GPG 签名
- 指定使用工作专用的 SSH 密钥进行认证

### 个人配置 ~/.gitconfig-personal

```ini
[user]
    name = "Your Personal Name"
    email = "personal@example.com"
    signingKey = "~/.ssh/id_personal.pub"

[commit]
    gpgsign = false

[core]
    sshCommand = "ssh -i ~/.ssh/id_personal -o IdentitiesOnly=yes"
```

个人项目配置类似，但使用：

- 个人邮箱
- 个人 SSH 签名密钥
- 可以选择不强制签名
- 使用个人 SSH 密钥进行认证

## hasconfig vs gitdir：该用哪个？

两种方式各有优劣：

| 特性              | gitdir           | hasconfig:remote.\*.url |
| ----------------- | ---------------- | ----------------------- |
| **判断依据**      | 文件系统路径     | 远程仓库 URL            |
| **最低 Git 版本** | 2.13+ (2017)     | 2.36+ (2022)            |
| **评估速度**      | 非常快           | 稍慢（需要扫描）        |
| **灵活性**        | 需要组织目录结构 | 位置无关                |
| **适用场景**      | 项目有固定目录   | 项目分散在各处          |

**推荐策略**：

如果你习惯将工作项目和个人项目放在不同的目录下，使用 **gitdir** 最简单：

```ini
[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work

[includeIf "gitdir:~/personal/"]
    path = ~/.gitconfig-personal
```

如果你的项目分散在各处，或者需要根据不同的 Git 托管平台区分身份，使用 **hasconfig:remote.\*.url** 更灵活：

```ini
[includeIf "hasconfig:remote.*.url:https://gitlab.company.com/*/**"]
    path = ~/.gitconfig-work

[includeIf "hasconfig:remote.*.url:https://github.com/personal/**"]
    path = ~/.gitconfig-personal
```

你也可以**两者结合使用**，兼顾简单性和灵活性。

## 常见问题

**问题 1：配置没有生效**

检查以下几点：

- 路径末尾是否有斜杠（`~/work/` 而不是 `~/work`）
- 配置文件路径是否正确（使用绝对路径更保险）
- Git 版本是否足够新（`git --version`）
- includeIf 是否放在了配置文件的末尾

**问题 2：hasconfig 模式不匹配**

```bash
# 检查远程 URL
git remote -v

# 检查 URL 格式
git config --get remote.origin.url
```

确保你的模式能正确匹配远程 URL 的格式。

**问题 3：多个配置冲突**

Git 配置的优先级从高到低：

1. 命令行参数（`git -c user.email=xxx@xxx.com`）
2. 本地仓库配置（`.git/config`）
3. includeIf 包含的配置
4. 全局配置（`~/.gitconfig`）
5. 系统配置（`/etc/gitconfig`）

后读取的配置会覆盖先读取的配置。

## 参考资料

- [Git 官方文档 - Conditional Includes](https://git-scm.com/docs/git-config#_conditional_includes)


---

> 作者: Nite  
> URL: https://www.nite07.com/zh-cn/posts/git-includeif/  

