Inspirer

使用 git subtree 集成项目到子目录

本文转载自 http://aoxuis.me/post/2013-08-06-git-subtree

使用场景

例如,在项目 Game 中有一个子目录 AI。Game 和 AI 分别是一个独立的 git 项目,可以分开维护。为了避免直接复制粘贴代码,我们希望 Game 中的 AI 子目录与 AI 的 git 项目关联,有 3 层意思:

  1. AI 子目录使用 AI 的 git 项目来填充,内容保持一致。
  2. 当 AI 的 git 项目代码有更新,可以拉取更新到 Game 项目的 AI 子目录来。
  3. 反过来,当 Game 项目的 AI 子目录有变更,还可以推送这些变更到 AI 的 git 项目。

用 git subtree 可以轻松满足上面的需求。

对比 git submodule

如果你没有用过 git submodule,你甚至可以不用了解 git submodule 是什么,submodule 的基本介绍也不在本文的说明范围内。虽然它满足了上述差不多的需求,但是复杂难用,以至于需要这么长一篇教程才能说清楚用法。

如果你的项目正在使用 git submodule,你应该知道用 submodule 有多么麻烦,这里还有一篇文章专门解释 git submodule 的缺点,可以参考前面教程里的最后一段来删除 submodule(是的,连删除步骤都非常麻烦)。

使用 git subtree 之后,管理、更新都更加方便。

什么是git subtree

git subtree 是一条 git 子命令,本质上 subtree 是一种合并策略,从 git v1.5.2,官方就推荐使用 subtree 代替 submodule,所以它并不需要保存 .submodule 这样的元信息。

git subtree 的前提条件

subtree 子命令很晚才集成到 git 中,请确保你的 git 版本(使用 git --version 查看) > v1.8.0.0。有些文章中说 v1.7.11 就已经集成了,实际上没有,如果直接执行会看到这样的结果:

$git subtree  
git: 'subtree' is not a git command. See 'git --help'.

如果你是在 OS X 下使用 git,推荐用 homebrew 来安装新版本

$brew install git  
$git --version  
git version 1.8.3.4

git subtree 用法

针对第一段的 3 条需求,我分别说明具体的命令。

1. 第一次添加子目录,建立与 git 项目的关联

建立关联总共有2条命令。

语法:git remote add -f <子仓库名> <子仓库地址>

解释:其中 -f 意思是在添加远程仓库之后,立即执行 fetch。

语法:git subtree add --prefix=<子目录名> <子仓库名> <分支> --squash

解释:--squash 意思是把 subtree 的改动合并成一次 commit,这样就不用拉取子项目完整的历史记录。--prefix 之后的 = 等号也可以用空格。

示例

$git remote add -f ai https://github.com/aoxu/ai.git  
$git subtree add --prefix=ai ai master --squash

2. 从远程仓库更新子目录

更新子目录有 2 条命令。

语法:git fetch <远程仓库名> <分支>

语法:git subtree pull --prefix=<子目录名> <远程分支> <分支> --squash

示例

$git fetch ai master  
$git subtree pull --prefix=ai ai --squash

3. 从子目录 push 到远程仓库(确认你有写权限)

推送子目录的变更有1条命令。

语法:git subtree push --prefix=<子目录名> <远程分支名> 分支

示例

$git subtree push --prefix=ai ai master

参考资料: