$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: subrepo (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
前言
submodule
目前对 git 仓库拆分的已有实现之一。环境git version 2.7.4.windows.1
准备工作
首先创建主仓库
subrepo-master
,随意提交一次文本,接着拉取到本地建立子仓库
subrepo
和subrepo1
,随意提交一次文本操作
在主仓库下运行如下命令后,可以看到在仓库中多出来文件
subrepo
以及.gitmodules
运行
git status
接着添加
submodule1
,并指定路径运行
git status
得到cat .gitmodules
得到这是一份子模块与路径的映射关系图,这份文件很重要,
git
根据这份文件去识别submodule
,所以这份文件应该被加入版本控制接着运行提交命令,可以看到三个目录都被添加到仓库了,注意子模块下面的文件并没有被添加进去。
160000
的含义是这是Git
中的一种特殊模式,基本上意味着您将提交记录为目录条目而不是子目录或文件。 然后提交到远端,就有了一个submodule
的仓库 :)接下来模拟多人协作,首先新建一个文件夹,运行命令后,会发现
subrepo
以及module/module1
目录并没有文件。这时候需要运行命令
git submodule init
去初始化本地配置文件以及git submodule update
拉取代码。通过 6、7 两步完成还是太过麻烦,好在有简单的命令,在
clone
那一步运行git clone --recursive
就会自动完成 6、7两步。如果只更改主仓库的代码,那各种操作大家都会。下面主要是子仓库远端修改、子仓库改分支、子仓库本地修改推送远端、主仓库下的子仓库的引用同时被多人修改等等。
子仓库远端修改
修改子仓库远端的内容,接着同步到本地。一种方式,进入子仓库目录
git fetch && git merge
,另一种方式git submodule update --remote [子仓库目录]
。 运行git diff
,可以看到子仓库改分支
在子仓库远端新建分支
a
,然后在主仓库运行命令,再运行cat .gitmodules
查看,发现subrepo
多了branch
指向。运行git submodule status
可以看到submodule
的状态。运行git log -p --submodule
可以查看到子模块的日志修改。同时可以将branch
指向tag
,但不支持commitid
。这里有一点,子模块记录的是commitid
即使指定了branch
,同时它不会主动升级(主动升级会导致每个人的代码不一致)。指向
commitid
的方案是运行如下指令,通过将这个子模块文件夹的commitid
指定为新的,即可完成指定相应commitid
的动作。此时观察.gitmodules
,会发现subrepo
指向的branch
字段消失。其他人在各自目录运行,即可获取最新更新
子仓库本地修改推送远端
子模块本地提交代码
当本地子模块没有指定
branch
的时候,是处于一个称作 “游离的 HEAD”(detached HEAD) 的状态(git 提示的是commitid
而不是分支名)。这个状态下你可以正常的git
操作,但是此时是没有分支进行跟踪的,也就没办法推送代码。想摆脱这种状态,在子模块运行git checkout branch
即可。子模块拉取服务端代码
然后对远端子模块做一次修改(称为 patchA),并运行
git submodule update --remote --merge
,就可以看到subrepo
发生了改变,此时可以提交这个改变,即将subrepo
指向的旧的commitid
换成patchA
的commitid
。后面的人更新代码的时候会拿到新的commitid
,如果没有人运行update
命令并提交,则所有人拿到的都是旧的代码。本地提交与远端合并代码
接下来对本地的
subrepo
做一次提交,接着对远端的子模块也做一次提交。然后更新子模块。可能会运行git submodule update --remote
,此命令只会更新成远端的代码,会发现本地的代码丢失,此时不需要慌张,运行git checkout branch
即可获取本地代码。合并的命令是git submodule update --remote
后面加上--merge
或者--rebase
即可,会遇到冲突的情况,进入目录手工解决即可。运行git diff -p --submodule
,可以查看到子模块的修改,现在修改的内容包含本地提交以及远端合并的代码,接下来就需要进行推送。本地提交到远端 在主仓库进行一次提交,提交子仓库最新的
commitid
。如果我们在主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到麻烦,因为他们无法得到依赖的子模块改动。那些改动只存在于我们本地的拷贝中。
为了确保这不会发生,你可以让 Git 在推送到主项目前检查所有子模块是否已推送。
git push --recurse-submodules=check
(如果子模块没有提交,会直接报错)或者git push --recurse-submodules=on-demand
(如果子模块没有提交,会尝试提交,提交不成功同时会阻止主仓库的推送)主仓库下的子仓库的引用同时被多人修改
比如
subrepo
指向的commitid
是a
,A 成员修改为b
, B 成员修改为c
,这时候就需要去合并代码,让其指向最新的a1
。如果
b
和c
是祖先关系,则 git 会直接快进式合并。下面模拟分叉的情况。检出一个本地仓库,
git reset --HARD oldcommitid
,然后将子模块的代码进行修改,接着添加到主仓库进行提交。接下来运行git pull
,会发现有冲突了。解决此问题的方案是,首先
git diff
查看一下代码找到冲突的
commitid
(其中 4660574 是公有的, 55167f5 是上游的),进入子模块,然后检出为分支git branch merge1 55167f5 && git merge merge1
,如果有冲突,此时就需要手动解决冲突了,解决完成,提交。再回到主目录,查看是否有冲突,对主仓库进行提交。此时再git pull
则无冲突,然后就可以用git push --recurse-submodules=on-demand
提交本次修改。删除子模块后会发现
.gitmodules
文件内容同时发生了改变。如果需要备份,请提前备份目录技巧
foreach
,可以在每一个子模块中运行任意命令。例如:保存进度,git submodule foreach 'git stash'
;切换分支git submodule foreach 'git checkout -b featureA'
等等,这个可以对子模块统一进行操作管理。git
命令别名,举例:这样运行更新命令的时候,就简化为
git supdate
,其他同理。git status
之类的获取的均是主仓库的状态,无法查看子模块的状态。建议git config --global status.submoduleSummary true
。git config --global diff.submodule log
可以查看到子模块的日志缺点
例如在有子模块的项目中切换分支可能会造成麻烦。 如果你创建一个新分支,在其中添加一个子模块,之后切换到没有该子模块的分支上时,你仍然会有一个还未跟踪的子模块目录,这时候如果不小心提交了这个子模块(
git commit -am "message"
),就会有问题了。对子模块的更新略显复杂,每次操作都需要所有人手动同步更新,增加了学习成本。
对子模块做了修改,需要先推送子模块再主模块,同时拉取的时候也需要先主模块,再子模块。
对子模块做本地修改需要先检出分支,否则有可能在 “游离的 HEAD” 上做修改。
删除子模块,需要的步骤有点复杂。
如果子模块被高频次更新,会有大量合并代码的工作,参考上面的 10-13
如果你的同事更新了
submodule
,然后更新了父项目中依赖的版本号。你需要在git pull
之后,调用git submodule update
来更新submodule
信息。这儿的坑在于,如果你git pull
之后,忘记了调用git submodule update
,那么你极有可能再次把旧的submodule
依赖信息提交上去(使用git submit -am "message"
或者git add .
提交的人会遇到这种事)。优点
不需要获取子模块整个代码库
主仓库只是获取到了子仓库的引用
参考
https://git-scm.com/book/en/v2/Git-Tools-Submodules
https://medium.com/@porteneuve/mastering-git-submodules-34c65e940407 最后的总结很好