Git基础——子模块
之前在搭建Hugo博客时,由于对Git子模块不熟悉,折腾了很久,本文对Git子模块进行基本了解
什么是Git子模块?
在一个项目中,有可能需要包含并使用另一个项目,它们虽然是两个独立的项目,但是处于一个项目中,为了避免混乱,Git引入了子模块submodule的概念
子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立
Git子模块基本使用
给项目添加子模块
将一个已存在的 Git 仓库添加为正在工作的仓库的子模块
git submodule add <GIT-PROJECT-URL>
添加成功后会在项目下创建一个.gitmodules
文件,该文件保存了子模块的基本信息,包括名称、path、url等,该文件也像 .gitignore
文件一样受到(通过)版本控制。 它会和该项目的其他部分一同被拉取推送
$ cat .gitmodules
[submodule "DbConnector"]
path = DbConnector
url = https://github.com/chaconinc/DbConnector
克隆含有子模块的项目
克隆含有子模块的项目,有两种方式
-
使用
git clone
克隆主项目,克隆后默认会包含该子模块目录,但子模块中没有任何文件,只是一个空目录git clone <GIT-PROJECT-URL>
必须运行两个命令:
git submodule init
用来初始化本地配置文件,而git submodule update
则从该项目中抓取所有数据并检出父项目中列出的合适的提交git submodule init git submodule update
-
另一个方法是克隆主项目时给
git clone
命令传递--recursive
选项,它就会自动初始化并更新仓库中的每一个子模块git clone --recursive <GIT-PROJECT-URL>
在包含子模块的项目上工作
拉取上游修改
如果想要在子模块中查看新工作,可以进入到目录中运行 git fetch
与 git merge
,合并上游分支来更新本地代码
$ git fetch
$ git merge origin/master
如果你不想在子目录中手动抓取与合并,那么还有种更容易的方式。 运行 git submodule update --remote
,Git 将会进入子模块然后抓取并更新。Git 默认会尝试更新所有子模块,所以如果有很多子模块的话,你可以传递想要更新的子模块的名字。
$ git submodule update --remote
上面的命令只会更新并检出子模块的master
分支,如果想要指定子模块的分支,如stable
分支,执行下面的设置
$ git config -f .gitmodules submodule.SUB_PROJECT.branch stable
$ git submodule update --remote
在子模块上工作
当我们运行 git submodule update
从子模块仓库中抓取修改时,Git 将会获得这些改动并更新子目录中的文件,但是会将子仓库留在一个称作 “游离的 HEAD” 的状态。 这意味着没有本地工作分支(例如 “master”)跟踪改动。 所以你做的任何改动都不会被跟踪
为了将子模块设置得更容易进入并修改,你需要做两件事。 首先,进入每个子模块并检出其相应的工作分支。 接着,若你做了更改就需要告诉 Git 它该做什么,然后运行 git submodule update --remote
来从上游拉取新工作。 你可以选择将它们合并到你的本地工作中,也可以尝试将你的工作变基到新的更改上
-
首先,进入子模块目录然后检出一个分支
$ git checkout master
-
然后尝试用 “merge” 选项。 为了手动指定它,我们只需给
update
添加--merge
选项即可$ git submodule update --remote --merge
-
如果我们现在更新子模块,就会看到当我们在本地做了更改时上游也有一个改动,我们需要将它并入本地
$ git submodule update --remote --rebase
-
如果你忘记
--rebase
或--merge
,Git 会将子模块更新为服务器上的状态。并且会将项目重置为一个游离的 HEAD 状态$ git submodule update --remote
出现这个问题,只需回到目录中再次检出你的分支(即还包含着你的工作的分支)然后手动地合并或变基
origin/stable
(或任何一个你想要的远程分支)就行了
如果你没有提交子模块的改动,那么运行一个子模块更新也不会出现问题,此时 Git 会只会抓取更改而并不会覆盖子模块目录中未保存的工作
发布子模块改动
如果我们在主项目中提交并推送但并不推送子模块上的改动,其他人尝试检出我们修改会遇到麻烦,因为他们无法得到依赖的子模块改动。 那些改动只存在于我们本地的拷贝中。
为了确保这不会发生,你可以让 Git 在推送到主项目前检查所有子模块是否已推送。 git push
命令接受可以设置为 “check” 或 “on-demand” 的 --recurse-submodules
参数。 如果任何提交的子模块改动没有推送那么 “check” 选项会直接使 push
操作失败
$ git push --recurse-submodules=check
另一个选项是使用 “on-demand” 值,Git 进入到 子模块中然后在推送主项目前推送子模块。 如果那个子模块因为某些原因推送失败,主项目也会推送失败
$ git push --recurse-submodules=on-demand
删除子模块
先反初始化,再删除子模块,最后提交。模块名后面不要加/
#反初始化
git submodule deinit <submodule>
#不保留在工作目录
git rm <submodule>
#保留在工作目录
git rm --cached <submodule>
#提交
git commit -am "Remove a submodule."
修改子模块URL
-
编辑
.gitmodules
文件 -
执行下列命令,执行后可以看到.git/config中url更新了
$ git submodule sync $ git commit -am "Update submodule url." # 提交变更
合并子模块改动
- 首先解决冲突
- 然后返回到主项目目录中
- 再次检查 SHA-1 值
- 解决冲突的子模块记录
- 提交我们的合并