引用一段《Git权威指南》的话:
项目的版本库在某些情况虾需要引用其他版本库中的文件,例如公司积累了一套常用的函数库,被多个项目调用,显然这个函数库的代码不能直接放到某个项目的代码中,而是要独立为一个代码库,那么其他项目要调用公共函数库该如何处理呢?分别把公共函数库的文件拷贝到各自的项目中会造成冗余,丢弃了公共函数库的维护历史,这显然不是好的方法。
为现有的仓库添加submodule,以第三方库CodeMirror为示例,在当前仓库下执行:
查看当前仓库的状态,注意到:submodule 和 .gitmodules两个文件已经自动被staged。
git status
其中.gitmodules保存了项目 URL 和你拉取到的本地子目录。如果有多个子module,则该文件会记录多个条目:
虽然 CodeMirror 是工作目录中的一个子目录,但 Git 还是会将它视作一个子模块。当你不在那个目录中时,Git 并不会跟踪它的内容, 而是将它看作该仓库中的一个特殊提交。
当你提交时,会看到类似下面的信息:
注意 CodeMirror 记录的 160000 模式。 这是 Git 中的一种特殊模式,它本质上意味着你是将一次提交记作一项目录记录的,而非将它记录成一个子目录或者一个文件。
最后推送至远端
git push -u origin master
你将得到了包含子项目的目录,但里面没有文件:
git clone http://git.xhuabu.com/chenrunfa/UI-module.git
需要通过两个命令来初始化:git submodule init
来初始化你的本地配置文件,git submodule update
来从那个项目拉取所有数据并检出你上层项目里所列的合适的提交:
或者将这两个命令写成一句话:git submodule update --init --recursive
但最快的方法是,克隆时连同所有子项目一起拉下来:
git clone --recursive http://git.xhuabu.com/chenrunfa/UI-module.git
删除子模块相对比较麻烦一些step by:
git rm -r --cached libs/
rm .gitmodules
,如果只是删除其中一个module,响应删除.gitmodules中对应条目即可场景:某个工作中的项目需要包含并使用另一个项目。 也许是第三方库,或者你
独立开发的,用于多个父项目的库。现在你想要把它们当做两个独立的项目,同时又想在 一个项目中使用另一个。
克隆一个带有子模块的工程git submodule update --init --recursive
直接在主仓库里抓取与合并子模块
git submodule update --remote
或者只更新指定的子模块
git fetch
git merge origin/master
在子模块上工作
当我们运行 git submodule update 从子模块仓库中抓取修改时,Git 将会获得这些改动并 更新子目录中的文件,但是会将子仓库留在一个称作 “游离的 HEAD” 的状态。 这意味着没有本 地工作分支(例如 “master”)跟踪改动。 所以你做的任何改动都不会被跟踪。
git checkout master 进入子模块并检出相应的工作分支
git submodule update --remote 从上游拉取数据
git submodule update --remote --merge 从上游拉取数据并合并
如果我们在主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到 麻烦,因为他们无法得到依赖的子模块改动。 那些改动只存在于我们本地的拷贝中。
为了确保这不会发生,你可以让 Git 在推送到主项目前检查所有子模块是否已推送。 git push 命令接受可以设置为 check 或on-demand 的 –recurse-submodules 参数。 如果任何提交的子模块改动没有推送那么 check 选项会直接使push 操作失败。
git push --recurse-submodules=check
git push --recurse-submodules=on-demand
案例1:当前企业正在开发Project1项目,Project1中除了自身源代码外还包含了一个名为CommonLib的submodule,此时,项目开发中途加入了两位新的开发人员(员工A和员工B),参与开发最首要的就是先搭建环境并获取当前项目的代码,才能投入开发状态;
A和B分别执行:git clone --recursive http://git.xhuabu.com/chenrunfa/UI-module.git
快速获取含有子模块的代码仓库。
案例2:在加入Project1项目的开发后,某一天,新员工A发现了CommonLib的代码中存在一个BUG或者是发现一个新的功能在其他项目也需要被用到,此时需要改动CommonLib中的源代码。
A在修复了BUG之后,如果按照直接执行在Project1目录下执行git commit –am “相关描述”进行提交,对submodule发生的改变是没有任何作用的,因为这种提交方式只是提交当前Project1主工程的修改,对于submodule发生的改变不会进行提交。
正确的提交方式先进入CommonLib子模块的目录
cd 子模块目录
git commit –am “相关描述”(如果增删了文件,需要先做git add .操作)
git rebase remotes/origin/master (同步服务器上的子模块代码)
git push(提交子模块到远程库)
cd 主工程目录
git add 子模块目录(将子模块发生修改告诉你的主工程,这样他才能进行跟踪)
git commit –am “相关描述”
git push (提交主工程到远程库)完成以上流程的操作,才算正确的提交了子模块的修改,并让主工程跟踪跟新子模块。
案例3:新员工A在更新的CommonLib的子模块后,其他一起开发Project1项目的人员(包括了新员工B)都都需要同步更新CommonLib中的代码。
此时新员工B需要执行如下的几个操作来:
git pull (在主工程目录下同步更新代码
git status(查看submodule是否发生了修改)
git submodule update(如果子模块发生修改,需要执行此操作进行更新)
如果子模块下还包含着子模块,最后一句的更新操作可以换成如下的命令:
git submodule foreach git submodule update
实际上,submodule项目和它的父项目本质上是2个独立的git仓库。只是父项目存储了它依赖的submodule项目的版本号信息而已。如果其他同事更新了submodule,然后更新了父项目中依赖的版本号。你需要在git pull之后,调用 git submodule update来更新submodule信息。
有些时候你需要对submodule做一些修改,很常见的做法就是切到submodule的目录,然后做修改,然后commit和push。但是默认git submodule update并不会将submodule切到任何branch,所以,默认下submodule的HEAD是处于游离状态的(‘detached HEAD’state)。所以在修改前,记得一定要用git checkout master将当前的submodule分支切换到master,然后才能做修改和提交。
如果你不慎忘记切换到master分支,又做了提交,可以用cherry-pick命令挽救。具体做法如下:
git log (获取当前在游离分支提交的commitid)
git checkout master(将HEAD从游离状态切换到 master 分支)
git cherry-pick 游离分支上提交的commitID (将提交从游离分支放到master分支上)
git push (将更新提交到远程版本库中)
以上案例来自:http://blog.csdn.net/d_clock/article/details/43730449
iOS项目中通过submodule管理library的例子:
http://blog.changyy.org/2012/12/ios-xcode-subprojects-git-submodule.html
git submodule搭配上Android studio的modules來管理source:
https://erttyy8821.gitbooks.io/android_memo/content/chapter1/SubmoduleAndModules.html
git submodule详细使用说明:
https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97