生活中的Design.

Git Submodule(子模块)

字数统计: 1.7k阅读时长: 6 min
2019/12/19 Share

缘起

之前在做flutter项目的时候小小接触过submodule,flutter项目里面ios/Android子项目都是git submodule,最近在写hexo博客的时候,需要用到自定义的主题,于是把themes下面的主题文件夹作为了submodule,所有又稍微研究了下.

介绍

有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目。 也许是第三方库,或者你独立开发的,用于多个父项目的库。 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。

我们举一个例子。 假设你正在开发一个网站然后创建了 Atom 订阅。 你决定使用一个库,而不是写自己的 Atom 生成代码。 你可能不得不通过 CPAN 安装或 Ruby gem 来包含共享库中的代码,或者将源代码直接拷贝到自己的项目中。 如果将这个库包含进来,那么无论用何种方式都很难定制它,部署则更加困难,因为你必须确保每一个客户端都包含该库。 如果将代码复制到自己的项目中,那么你做的任何自定义修改都会使合并上游的改动变得困难。

Git 通过子模块来解决这个问题。 子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。
链接Git-Scm

基本操作

1.添加子模块

1
$ git submodule add [仓库链接]

默认情况下会在当前项目下生成一个子模块的repo同名的目录,如果想要放到其他目录可以在命令后添加一个路径.

同时也会生成一个.gitmodules文件,里面记录了子模块的名称,路径和仓库地址(该文件也需要加入版本控制),

git会把子模块目录视作模块而不是目录,不会跟踪其中的内容,提交时Git会以一种特殊方式提交(160000模式),本质上意味着你是将一次提交记作一项目录记录的,而非将它记录成一个子目录或者一个文件。

2.克隆含有子模块的项目

用寻常的git clone 命令克隆之后,项目中会有子模块目录,但是里面不会有任何文件

需要下面的两个命令来初始化本地配置文件,拉取数据

1
$ git submodule init
1
$ git submodule update

当然也有更简单的方式,在git clone 时加入--recursive 选项,就会更新仓库中所有的子模块.

如何使用

1.只拉取上游修改

在项目中使用子模块的最简模型,就是只使用子项目并不时地获取更新,而并不在你的检出中进行任何更改。 我们来看一个简单的例子。

如果想要在子模块中查看新工作,可以进入到目录中运行 git fetchgit merge,合并上游分支来更新本地代码。

如果你不想在子目录中手动抓取与合并,那么还有种更容易的方式。 运行 git submodule update --remote [模块名称],Git 将会进入名称对应子模块然后抓取并更新。

当运行 git submodule update --remote 时,Git 默认会尝试更新所有子模块

此命令默认会假定你想要更新并检出子模块仓库的 master 分支。 不过你也可以设置为想要的其他分支。 例如,你想要在子模块跟踪仓库的 “stable” 分支,那么既可以在 .gitmodules 文件中设置(这样其他人也可以跟踪它),也可以只在本地的 .git/config 文件中设置。 让我们在 .gitmodules 文件中设置它:

1
$ git config -f .gitmodules submodule.[模块名称].branch stable

如果不用 -f .gitmodules 选项,那么它只会为你做修改。但是在仓库中保留跟踪信息更有意义一些,因为其他人也可以得到同样的效果。

2.在子模块上修改

到目前为止,当我们运行 git submodule update 从子模块仓库中抓取修改时,Git 将会获得这些改动并更新子目录中的文件,但是会将子仓库留在一个称作“游离的 HEAD”的状态。 这意味着没有本地工作分支(例如 “master” )跟踪改动。 所以你做的任何改动都不会被跟踪。

为了将子模块设置得更容易进入并修改,你需要做两件事。 首先,进入每个子模块并检出其相应的工作分支。 接着,若你做了更改就需要告诉 Git 它该做什么,然后运行 git submodule update --remote 来从上游拉取新工作。 你可以选择将它们合并到你的本地工作中,也可以尝试将你的工作变基到新的更改上。

进入子模块checkout一个工作分支stable

1
git checkout stable

合并服务器上的修改(在主项目目录中)

1
$ git submodule update --remote --merge

如果本地和服务端都有修改

1
$ git submodule update --remote --rebase

如果你忘记 --rebase--merge,Git 会将子模块更新为服务器上的状态。并且会将项目重置为一个游离的 HEAD 状态。

即便这真的发生了也不要紧,你只需回到目录中再次检出你的分支(即还包含着你的工作的分支)然后手动地合并或变基 origin/stable(或任何一个你想要的远程分支)就行了。

如果你没有提交子模块的改动,那么运行一个子模块更新也不会出现问题,此时 Git 会只抓取更改而并不会覆盖子模块目录中未保存的工作。

3.提交子模块的改动

你可以让 Git 在推送到主项目前检查所有子模块是否已推送。 git push 命令接受可以设置为 “check” 或 “on-demand” 的 –recurse-submodules 参数。 如果任何提交的子模块改动没有推送那么 “check” 选项会直接使 push 操作失败。

1
$ git push --recurse-submodules=check

或者直接使用on-demond选项尝试push更改,Git 会进入到子模块中然后在推送主项目前推送它。 如果那个子模块因为某些原因推送失败,主项目也会推送失败。

1
$ git push --recurse-submodules=on-demand

其他

有一个 foreach 子模块命令,它能在每一个子模块中运行任意命令。 如果项目中包含了大量子模块,这会非常有用。

1
$ git submodule foreach 'git checkout -b dev'
CATALOG
  1. 1. 缘起
  2. 2. 介绍
  3. 3. 基本操作
    1. 3.0.1. 1.添加子模块
    2. 3.0.2. 2.克隆含有子模块的项目
  • 4. 如何使用
    1. 4.0.1. 1.只拉取上游修改
    2. 4.0.2. 2.在子模块上修改
    3. 4.0.3. 3.提交子模块的改动
  • 5. 其他