当我们多个项目都需要依赖一个公共库的时候,以往的做法是把这个公共库文件复制到项目中,如果有N个项目需要依赖的话,就要复制N次,当这个公共库有更新的时候,又要给每个依赖的项目重新复制更新,十分麻烦。
当我们使用了git submodule之后,一切都变得简单且清晰,我们主项目并不需要负责子模块的维护,只是在需要的时候同步一下子模块的更新即可。
本文只是介绍git submodule的简单使用,详情请移步官方文档。
一、起步
首先在github创建三个远程仓库father、son、daughter, 其中把father作为主体库,然后本地clone下来,每个项目添加一个描述文件。
二、添加子模块
接下来我们为father添加son和daughter两个子模块,命令为git submodule add
可以看到father下面多出了三个文件
其中.gitmodules文件记录了子模块的信息,相当于户口本,如下
此时father本地已经添加了两个子模块,我们现在将其提交到远程仓库(相当于发朋友圈告诉别人我有儿子和女儿了)
此时去远程仓库中可以看到我们已经提交成功了,但是会发现son和daughter目录后面跟着一串数字,这是怎么回事呢?其实我们远程仓库中并不会保存引入的第三方库的实体文件,而是通过.gitmodules文件存储和子模块的联系,son目录后面的数字代表的是son子模块的commitId,当我们点击son的时候,我们会跳转到son仓库对应的commitId版本上。
本地删除father,重新clone
此时会发现我们clone下来的father中,son和daughter是空的文件夹,之前说了远程仓库是不会保存我们引入子模块的实体文件,所以我们需要手动本地拉取。命令为git submodule update --init --recursive,-- recursive表示递归拉取,即子模块中存在引入其他子模块的情况,统统一次性拉取好。
其中还有一个命令是
这个命令表示根据我们的.gitmodules文件的描述,拉取子模块对应分支的最新commit版本,而--init则会拉取我们远程仓库保存的子模块commitId对应的版本。
三、更新子模块、子模块切换分支
当我们进入son子模块,会发现头指针处于一个commitId上,我们切换到一个adult分支,然后修改了son.txt文件,我们需要本地提交一下,并推到远程仓库中。
进入father,发现son已经更新了一个提交,此时在father中也要commit一次,为了更新father的版本控制和指向son的指针。
此时我们到远程仓库中可以发现,son对应的commitId链接已经变了,说明我们已经成功更新了father中的son子模块
在本地执行git submodule命令,可以看到子模块对应的分支和commitId
这里需要注意的是,我们son子模块发生更新之后,仅仅在本地commit,不将其push到远程仓库上,而father发现son变化后add、commit、push到远程仓库上,在这种情况下,别人clone father后,git submodule update --init 会失败,因为son远程仓库并没有与father指定的son commitId的版本。
四、子模块添加子模块
与之前father添加子模块类似,这里不再赘述。
五、删除子模块
(1)删除.gitmodules文件中相关的子模块条目
(2)删除项目中子模块目录
(3)删除.git/modules中子模块文件夹
(4)删除.git/config文件中子模块相关条目
或者
(1)git submodule deinit 子模块(手动实验了一下,执行这个命令之后.gitmodules文件并不会改变, 只是清空了子模块文件夹,子模块文件夹本身并不会被删除,并且会清空 .git/config文件中子模块相关条目)
(2)删除项目中子模块目录
(3)删除.git/modules中子模块文件夹
最后git add、git commit、git push提交到远程仓库即可