人与人之间的交互是复杂的,并且其效果从来都难以预期,但却是工作中最为重要的方面。
在很多大型项目中,需要在多个仓库中共享代码。这些代码可能是通信协议、公用代码库、第三方代码库等。如何管理共享代码是个头疼的问题,我们总不能在每个仓库中拷贝这些共享代码。git提供了submodule和subtree两种方案来方便我们管理。我们来进行对比,选择合适的方案加入到项目中。
git submodule
submodule是一个完全独立的仓库,引用submodule的父仓库和submodule的唯一联系,是保存在父仓库中子仓库某个commit-SHA
值。我们通过在父仓库中执行add
和update
来增加和更新submodule。
1 | # test repository |
执行add
操作后,在test目录下会产生一个.gitsubmodule
文件,其内容如下:
1 | $ cat .gitsubmodule |
针对于频繁更新的共享代码仓库,submodule的缺陷在于其管理的复杂性。其复杂性体现在三个方面:
1、更新代码的复杂性
如果我们需要在submodule中提交代码,我们需要执行四步:1、切换到开发分支;2、pull最新代码;3、提交代码到submodule仓库;4、从父仓库中提交submodule当前commit-SHA
值。切换到开发分支是很重要的,否则我们很可能会丢掉代码。同时,如果我们存在多个父仓库,每个父仓库都要提交submodule的当前commit-SHA
值。
如果我们需要获取最新的submodule,我们需要执行两步:1、拉取父仓库代码;2、运行git submodule update
命令。
2、团队协作的复杂性
在更新代码时,如果团队成员忘记其中的任何一步,对其他成员将会存在一定的影响。举两个例子:
拉取最新代码时,如果我们忘记运行
git submodule update
命令,之后更新代码,很有可能在父仓库中提交了老的submodulecommit-SHA
值。
如果团队成员对子模块做了一个本地的变更,但没有推送到公共公共服务器,然后他们提交了一个指向那个非公共状态的
commit-SHA
值,并推送到了父项目所在仓库。这时,其他开发者试图运行git submodule update
就会提示找不到所引用的子模块提交。
3、代码合并的复杂性
在合并代码时,即使我们解决了submodule的冲突,还是需要执行git submodule update
命令。
如果我们的共享代码仓库是稳定的第三方库,submodule是个极好的选择。然而,针对于频繁更新的共享代码仓库,使用submodule时无法避免其复杂性,但是可以通过一些手段,让我们不容易出错。
git submodule trick
约定
- master分支上作为submodule发布分支(开发分支遵循checkout/merge原则)
- master分支上最新的提交必须可以被父仓库引用
初次clone
使用recursive
参数来自动clone submodule仓库。
1 | # unpleasure method |
更新代码
使用foreach
将所有submodule切换到master分支、拉取最新代码,然后在父仓库提交submodule的commit-SHA
值。
1 | $ git submodule foreach --recursive git checkout master && git pull |
当我们需要更新仓库中的submodule,foreach
可以简化我们的工作量。
如果某些submodule并没有使用master分支作为发布分支,我们采用下列命令:
1 | git submodule foreach -q --recursive \ |
采用这个命令的前提是在添加submodule时指明分支:
1 | # add submodule to track master branch |
提交代码
submodule代码会被多个父仓库引用,所以需要在每个父仓库中验证修改是否有效。在验证有效后,从开发分支merge到master分支。
错误提交补救
如果我们没有checkout master分支,却又提交了代码,可以使用cherry-pick
命令提取错失的提交:
- 执行
git checkout master
将HEAD从detached状态切换到master分支,记录git报出的warning中的commit-SHA
; - 执行
git cherry-pick commit-SHA
提取错失的提交到master分支; - 执行
git push
重新提交代码。
如果需要提交代码,必须按照使用
foreach
指令,确保每个submodule都在正确的分支、最新的提交。如果出现本节这种情况,说明开发者没有执行正确的使用步骤。
git subtree
subtree对于父仓库来说是完全透明的,所有开发人员看到的是一个项目中的普通目录,开发人员无须针对subtree做特殊的处理。只需要维护subtree的开发人员在合适的时候去执行代码同步操作。我们在父仓库中通过add
、push
和pull
来操作subtree:
1 | $ git remote add -f sub git://github.com/sub/sub.git |
subtree是git官方网站推荐使用的方案。subtree的优点在于简单,复杂度低。过于简单也是subtree的缺点:
- 我们无法在subtree目录中查看对应的commit信息;
- subtree的提交会污染父仓库的提交记录。
未完待续。