Git Subtree使用演示

Git Subtree是Git自带的命令,用于公共库的模块化,并共享给多个项目。

1. 前提

假设有两个主项目:B1和B2。分别存在两个独立的Git库中。

其中B1有一个模块C1,将来可以在B2中使用,宜做成公共模块。目前状况,已经实现了B1的全部,和B2不带C1的部分。


image.png

2. 目标

公共模块独立成一个单独的库,且B1,B2复用此库,在其中一个地方更新,可以同步到所有项目。


image.png

3. 将公共模块从主项目中分离出来

git subtree split

# B1项目包含两个模块:B1文件夹和C1文件夹
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B1 (develop)
$ ls
B1/  C1/
 
# 将子模块分离出来,并且重新合并到原项目中。分离出来的子模块同时也存在于一个独立的branch中
# --prefix= 也可以用 -P 替代,--branch= 也可以用 -b 替代
# 使用--rejoin 参数,这样分离后的公共库会自动合并到原项目中
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B1 (develop)
$ git subtree split --prefix=C1 --branch=temp_br_for_C1 --rejoin
Merge made by the 'ours' strategy.
Created branch 'temp_br_for_C1'
f7cc8ba379d95faf11529e4abeb1d61f22f231f7

此时B1的状态变成了这样,多了一个分支,原来分支内容没变


image.png

观察历史记录,可以看到多了两个commit


image.png

4. 把公共模块独立存成一个库

# 随便找个地方建立一个空的库,这个路径无所谓,也可以是在Gitlab网上建 
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B1 (develop)
$ cd ..
 
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos
$ mkdir CommonModule_C1
 
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos
$ cd CommonModule_C1/
 
# 建立空的库
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/CommonModule_C1
$ git init
Initialized empty Git repository in D:/tmp/subtree/local_repos/CommonModule_C1/.git/
 
# 把刚才B1中的另一个分支pull到这个空的库中
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/CommonModule_C1 (master)
$ git pull ../MainProj_B1 temp_br_for_C1
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 221 bytes | 27.00 KiB/s, done.
From ../MainProj_B1
 * branch            temp_br_for_C1 -> FETCH_HEAD

这样我们多了一个C1库。有需要的话,也可以把这个库push到远程服务器,这属于Git通用操作,这里就不叙述了。


image.png

这步骤操作以后,可以把B1中的这个branch删掉了(后面也不会再用到)

twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B1 (develop)
$ git br -d temp_br_for_C1
Deleted branch temp_br_for_C1 (was f7cc8ba).

5. 将B1与C1库关联

其实这个步骤没有意义,B1和C1本身就是关联的,其奥秘就在于B1主分支中多出来的两个commit。
看看C1库的sha号,就是上面B1关联的那个。

image.png

当然我们也可以再确认一下
git subtree pull

# 尝试pull一下,结果发现B1果然没有什么变化。
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B1 (develop)
$ git subtree pull --prefix=C1 ../CommonModule_C1/.git master
From ../CommonModule_C1/
 * branch            master     -> FETCH_HEAD
Subtree is already at commit f7cc8ba379d95faf11529e4abeb1d61f22f231f7.

因此这一步,我们什么都没做,其实已经是这样的关系。


image.png

6. 在B2中关联C1库

下面我们要在B2中也用这个公共模块。
git subtree add

# 从库C1中的master分支,取公共模块并放到B2项目的C1子目录
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B2 (develop)
$ git subtree add --prefix=C1 ../CommonModule_C1/.git master
git fetch ../CommonModule_C1/.git master
From ../CommonModule_C1/
 * branch            master     -> FETCH_HEAD
Added dir 'C1'

此时B2项目的log,跟B1项目很像。


image.png

也就是完成了这样的操作


image.png

至此,综合起来,这3个库就形成了关联。用这种方法,可以关联更多的项目,没有数量限制。
image.png

7. 在一个项目改动后的push和pull

假设在B2项目,我们改动了公共模块C1并提交了。

# 修改文件内容
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B2/C1 (develop)
$ echo "add by B2" >> CC1.txt
...
# 提交等操作
...
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B2/C1 (develop)
$ git ci -m "modify C1 by B2"
[develop 25fad03] modify C1 by B2
 1 file changed, 1 insertion(+)

此时B2的修改记录如下

image.png

将改动推送到C1库中
git subtree push

# 推送发现出错了,意思是,推送只能推送到bare类型的库中
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B2 (develop)
$ git subtree push --prefix=C1 ../CommonModule_C1/.git master
git push using:  ../CommonModule_C1/.git master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 278 bytes | 278.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
...

推送发现出错了,意思是,推送只能推送到bare类型的库中,因此把C1库再加工一下。
注意如果C1是在Gitlab的话,就没这个问题,因为Gitlab上面的库全部都是bare类型的。

# 创建一个空的bare库
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos
$ git init CommonModule_C1_bare.git --bare
Initialized empty Git repository in D:/tmp/subtree/local_repos/CommonModule_C1_bare.git/
 
 
# 把C1库推送到这个新的库
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/CommonModule_C1 (master)
$ git push ../CommonModule_C1_bare.git master
Everything up-to-date

这步结束以后,原来的C1库也可以删掉了。当然也可以留着单独修改C1模块用。


image.png

此时再次将B2中的修改推送到C1库中

# 再次推送,这次就推送成功了
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B2 (develop)
$ git subtree push --prefix=C1 ../CommonModule_C1_bare.git master
git push using:  ../CommonModule_C1_bare.git master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 278 bytes | 278.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../CommonModule_C1_bare.git
   f7cc8ba..1bfb723  1bfb7238dd929509b40224447f2c871f6d5e445a -> master

此时查看C1库,看到多了一条提交。

image.png

注意:这条提交,还没有跟B2库产生关系。还需要Git subtree pull一下
git subtree pull

# 注意这里pull,会出现一个merge
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B2 (develop)$ git subtree pull --prefix=C1 ../CommonModule_C1_bare.git master
From ../CommonModule_C1_bare
 * branch master -> FETCH_HEAD
Merge made by the 'recursive' strategy.

注意看这时候的B2的历史记录,可以发现两个现象

最新的提交,确实与C1库最新改动产生了关联(红色)
存在两个一样的提交(绿色)
其中2是正常现象,不必奇怪,因为Git Subtree中,主项目和公共项目本来就是两个独立的库,修改也是独立的。其中一条是主项目的修改,另一条是公共项目的修改。虽然存在两个提交,但是不会冲突,因为本来就是来源相同的。


image.png

8. 在其他项目中同步公共库的修改

在B1项目中,将公共库的改动同步下来
git subtree pull

# 执行的命令与刚才完全相同
twu@twu-PC MINGW64 /d/tmp/subtree/local_repos/MainProj_B1 (develop)
$ git subtree pull --prefix=C1 ../CommonModule_C1_bare.git master
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 258 bytes | 19.00 KiB/s, done.
From ../CommonModule_C1_bare
 * branch            master     -> FETCH_HEAD
Merge made by the 'recursive' strategy.
 C1/CC1.txt | 1 +
 1 file changed, 1 insertion(+)

可以看到B1已经同步了修改。B1的提交比较正常,不会出现重复的两条提交,因为并不是在B1这个项目中修改的。


image.png

9. 其他补充说明

  1. C1库也可以单独下载下来,直接在单独的库里修改并提交。这个库独立来看,跟普通的Git库毫无差异。
  2. B1,B2库以后也可以再也不同步C1的改动,它们完全独立存在,即使C1删掉,对它们也没什么影响。
  3. 这里C1的库我们全部是在master分支进行的修改,实际上也可以分出多个分支来,更好的管理。

10. 思考题

Git subtree这种方式,比我们自己维护一个独立的公共库,然后手动拷贝到各个项目中,有什么区别,有什么优势?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容