git: 使用submodule进行托管

1. 问题描述:

假设我们在 root-project 里需要添加3个submodules:A和B是根项目的子模块。 C又是项目A的子模块。

root-project
      |
      |-- A
      |   |
      |   |-- C
      |
      |-- B

 

2. 准备工作

mkdir repository             // 模拟远程仓库位置

// 模拟远程子模块A&B
git init --bare repository/submodule_A.git
git init --bare repository/submodule_B.git

// 初始化子模块A&B用于测试
mkdir init
cd init
git clone ../repository/submodule_A.git
git clone ../repository/submodule_B.git
echo "" > submodule_A/"This is submolue A".txt
echo "" > submodule_B/"This is submolue B".txt

cd submodule_A
git add .
git commit -m "init"
git push

cd submodule_B
git add .
git commit -m "init"
git push

// 模拟工程目录
git init --bare repository/prj.git
git clone repository/prj.git prj/myprj        // 指定路径的话会修改*.git原来目录的名称
cd prj/myprj/
echo "" > "This is root project".txt

 

目录结构:

.
|-- init
|   |-- submodule_A
|   `-- submodule_B
|-- prj
|   `-- myprj
`-- repository
    |-- prj.git
    |-- submodule_A.git
    `-- submodule_B.git

 

3. 使用submodule进行管理

添加子模块A&B

// 使用*.git默认的名称你需要先切换到想放置的目录
git submodule add ../../repository/submodule_A.git/

// 重新命名文件夹,命名“submodule_B” 为 “renamedB”
git submodule add ../../repository/submodule_B.git/  renamedB

 

 

结果示意:

$ tree
.
|-- This is root project.txt
|-- renamedB
|   `-- This is submolue B.txt
`-- submodule_A
    `-- This is submolue A.txt

 

4.  submodule修改提交

4.1 提交修改

切换到submodule的路径下,和git的使用方式相同:

$ cd submodule_A/
$ echo "" > submodule_A/"modified in A".txt
$ git add .
$ git commit -m "modified"

 

 

4.2 更新到远程分支

第1种是直接push:

$ git push

 

 

试想一下:

1. 如果你一个工程里有多个submodules,虽然你每次修改子模块时都有commit;

2. 但是因为prj没有完成,或者子模块还没有修改到可以发布的程度,所以你每次commit 后并不想更新远程分支。

3. prj工程已经完成,可以push工程里所有的submodule到远程分支了;但是如果一个个路径再打开push,太麻烦。

这时就可以使用递归的方法:

// 模拟修改过程
$ cd submodule_A/
echo "" > "modified 2 in A".txt
$ git add .
$ git commit -m "modified 2"

$ cd ../renamedB/
echo "" > "modified 2 in B".txt
$ git add .
$ git commit -m "modified 2"

// 提交prj修改记录
$ cd ..
$ git add .
$ git commit -m "update now"

 

 

第2种:使用递归push:

git push --recurse-submodules=check  // 如果子模块没有提交,会直接报错

// or
git push --recurse-submodules=on-demand  // 如果子模块没有提交,会尝试提交,提交不成功同时会阻止主仓库的推送

 

 

 

4.  带有submodule仓库的克隆方法

  1. 直接clone的话是没有submodule信息的,只有文件夹。

mkdir local
$ git clone ../repository/prj.git cloneDirect
$ cd cloneDirect/
$ tree -a -L 2
.
|-- .git
|   |-- HEAD
|   |-- config
|   |-- description
|   |-- hooks
|   |-- index
|   |-- info
|   |-- logs
|   |-- objects
|   |-- packed-refs
|   `-- refs
|-- .gitmodules
|-- This is root project.txt
|-- renamedB
`-- submodule_A

8 directories, 7 files

 

  这是因为,父项目的git并不会记录submodule的文件变动,它是按照commit id指定submodule的git header。

   不过可以先对submodule初始化,然后更新,就可以clone到原module的内容。

  

  2. 第二种方式使用递归clone,添加参数 --recursive

  

在远程修改submodule及本地更新submodule

  1. 远程更新submodule

  

  2. 本地更新submodule

  2.1 直接pull或者更新submodule是无效的,因为远程prj.git并未记录submodule有任何的改动,即远程prj.git不知道也根本不管submodule有没有更新,除非你在prj.git有commit记录。

  

  2.2 在这种情况下仍要更新submodule,就需要直接进入submodule仓库下并切换为需要的分支进行更新。

  

  2.3 这时prj.git 理所当然地发生了变化,根据需要提交变更记录即可。

  

在本地修改submodule并push后,他处submodule更新流程

  1. 本地A修改submodule并Push

    1.1. 现在本地submodule添加一个新功能:c.md,然后push。

   

    1.2 接着,提交本次prj.git的变更记录:

   

   2. 他处B更新submodule

    2.1 pull后发现submodule发生了改动:

    

    2.2 使用update对submodule进行更新

    

删除submodule

  1.  git rm moduleA,即可

  

  这样做git的config文件中仍有相关记录,但是不影响使用,如果新clone的话,就不会有相关的记录了。

  如果介意可以手动将该文件的相关行删除。

  

  2. 新clone后,config则无相关记录

  

 

 

 

 

echo "" > "This is root project".txt
posted @ 2018-11-07 23:52  QIYUEXIN  阅读(313)  评论(0编辑  收藏  举报