# Subtree与Submodule

## 两者区别

| 维度     | subtree                                                      | submodule                                                    | 优劣对比       |
| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------------- |
| 空间占用 | subtree 在初始化 add 时，会将子仓库 copy 到父仓库中，并产生至少一次 merge 记录。所以会占用大量父仓库空间 | submodule 在初始化 add 时，会在父仓库新建一个 .gitmodules 文件，用于保存子仓库的 commit hash 引用。所以不会占用父仓库空间 | submodule 更优 |
| clone    | subtree add 至父仓库之后，后续的 clone 操作与单一仓库操作相同 | 后续 clone 时 submodule 还需要 init/update 操作，且 submodule 子仓库有自己的分支 | subtree 更优   |
| update   | 子仓库更新后，父仓库需要 subtree pull 操作，且命令行略长，需要指定 --prefix 参数。由于无法感知子仓库的存在，可能会产生 merge 冲突需要处理 | 子仓库更新后，父仓库需要 submodule update 操作。父仓库只需变动子仓库 hash 引用，不会出现冲突 | submodule 更优 |
| commit   | 父仓库直接提交父子仓库目录里的变动。若修改了子仓库的文件，则需要执行 subtree push | 父子仓库的变动需要单独分别提交。且注意先提交子仓库再提交父仓库 | subtree 更优   |

总结：

- `git subtree`：将一个已有的项目作为一个子模块集成到其他项目中，或者将一个项目的某个子目录独立出来作为一个新的项目进行管理。

  适用场景：更适合管理已有的项目之间的依赖关系；

  区别：父仓库会跟踪子仓库中的文件变化，子仓库的变化会直接影响父仓库；

- `git submodule`：将一个 仓库作为子模块嵌入到另一个仓库中。子模块的代码是独立管理的，可以在父仓库中指定子模块所需的版本，并且可以在子模块中进行开发和提交。

  适用场景：更适合管理独立的子仓库；

  区别：父仓库不会跟踪子仓库中的文件变化，子仓库的变化需要单独处理；




## Subtree 命令行简化

subtree 在操作时，命令行较长，可以使用 remote 配置简化，例如

```
# 以下为标准 subtree add 命令行示例
git subtree add --prefix=centos-config --squash git@github.com:toimc/centos-config.git master

# 可以简化为
# 1. 先为远程子仓库配置一个别名，便于后续的 pull 与 push 操作，这里例子以 centos 为别名
git remote add centos git@github.com:toimc/centos-config.git 

# 2. 其中 --prefix= 简写为 -P，配置 --squash 表示不拉取子仓库的历史提交记录
git subtree add -P centos-config --squash centos master

# 后续更新子仓库可以使用
# 若发生 fatal: refusing to merge unrelated histories 报错，加上 --squash 参数即可
git subtree pull -P centos-config centos master
```

