由于需要在项目中内嵌另一个项目的代码,而两个项目是由各自的 git 仓库分别管理的,手动复制更新异常繁琐,因而可以使用 git 的子模块来解决该问题。
引入子模块
在父 git 仓库下添加配置文件 .gitmodules
,文件内容配置子模块的相关配置:
1 | [submodule "src/b"] |
如上述示例,该配置文件指定子模块 b,以及在父仓库下检出的路径。分支是可选的,这样在初始化的时候会检出特定分支。
这里需要注意的一点,url 采用相对路径,是因为在项目中使用了 gitlab 的 ci,在父仓库需要检出子模块的时候,需要有子仓库的权限,查阅 gitlab 的官方文档,需要采用相对路径的配置。如果不需要跑 CI,那么就无所谓了,这里的 url 可以使任何合法的 git 仓库地址。
初始化
在其他项目成员拉代码的时候,需要执行如下命令来初始化子模块仓库:
1 | git clone --recursive http://git/a.git |
传递 —recursive
参数,在 clone 代码的时候就会初始化仓库中定义的子模块。如果子模块是在后续已克隆代码后引入的,那么需要执行:
1 | git submodule update --init --recursive |
处理冲突
父仓库中的子模块的当前状态同父仓库中的某一个 commit 绑定,比如在父仓库的 develop 分支,子模块的 id 为 a,master 分支子模块的 id 为 b。那么,从 develop 分支切换到 master 分支时,分支上记录的当前子模块的 commit id 就会发生变化,因而会显示子模块仓库下的文件有更改需要提交。这个时候需要执行如下命令,来检出当前分支记录的子模块状态:
1 | git submodule update |
同理,在更新代码后,如果子模块在远程仓库被更新,那么拉下最新的代码后就需要重新检出相应的子模块代码。这未免繁琐,项目中的其他成员如果有子模块冲突,没有执行该命令,那么极有可能导致子模块的 commit id 被覆盖,造成子模块回退。
所以,有两种方案,一种是给 git 取别名,替换之前使用的 pull 和 checkout 等命令,在其中增加子模块的更新操作。我认为这种方案不合理,有点蹩脚。
更合适的方案是使用 hook,在特定操作后自动执行。由于是在前端项目,可以方便的使用 husky 来添加 hook,这里其实只需要添加两个 hook,一个是在 merge 操作后,另一个是在 checkout 操作后,这两个操作都代表当前的代码状态发生里变化,意味着子模块的 commit id 有可能被更新,需要检出特定的子模块状态。我的配置如下:
1 | "husky": { |
放在 package.json
中,这样便可保证子模块更新的执行。
子模块协作
关于子模块在父项目中进行子模块的代码更新,在当前的项目中并没有使用到,也是不被允许的操作,暂时没有更好的想法,有需要的时候实践再分享下更好的解决方案。
-EOF-