Git 工具 - 子模块: submodule与subtree的使用
git日常使用中,基本都是一个项目一个Git仓库的形式,那么当我们的代码中碰到了业务级别的需要复用的代码,我们一般怎么做呢?
比如:某个工作中的项目需要包含并使用另一个项目。 也许是第三方库,或者你独立开发的,用于多个父项目的库。
所以需要提取一个公共的类库提供给多个项目使用,但是这个library怎么和git在一起方便管理呢?
现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。
我们大致的考虑一下,一般有两种方案:
抽象成NPM包进行复用;
使用Git的子仓库对代码进行复用;
但是:两个程序中有部分页面或功能是重叠的,在前端领域,比如AntDesign、element-UI,react 、vue、angular版本样式是一样的,只是组件不同。
开发过程中重叠部分如果开发两套代码会浪费不少的人力。
个人推荐选用Git子模块的方式进行开发,父级仓库依赖两个公共的子模块,子模块本身和父级仓库一同进行开发,可避免了版本问题和重复开发的问题。
面对比较复杂的项目,我们有可能会将代码根据功能拆解成不同的子模块。主项目对子模块有依赖关系,却又并不关心子模块的内部开发流程细节。
大致结构可能是这样
project
|--moduleA
|--submoduleC
|--submoduleD
|--moduleB
project和ABCD各个模块中,CD在不同的git仓库中,这时,就需要使用git的的模块功能
Git 工具 - 子模块
Git 通过子模块来解决这个问题。 子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。
通俗上的理解, 一个Git仓库下面放了多个其他的Git仓库,其他的Git仓库就是我们父级仓库的子仓库。
git Submodule 是一个很好的多项目使用共同类库的工具,他允许类库项目做为repository,子项目做为一个单独的git项目存在父项目中,子项目可以有自己的独立的commit,push,pull。而父项目以Submodule的形式包含子项目,父项目可以指定子项目header,父项目中会的提交信息包含Submodule的信息,再clone父项目的时候可以把Submodule初始化。
可以端详官网:https://git-scm.com/book/zh/v2/Git-工具-子模块
多个父级仓库都依赖同一个子仓库,但是子仓库自身不单独进行修改,而是跟随父级项目进行更新发布,其他依赖子仓库的项目只负责拉取更新即可。
Git两种子仓库使用方案
git submodule
git subtree
git submodule(子模块)
Git子模块允许我们将一个或者多个Git仓库作为另一个Git仓库的子目录,它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。
在Git 中你可以用子模块submodule来管理这些项目,submodule允许你将一个Git 仓库当作另外一个Git 仓库的子目录。这允许你克隆另外一个仓库到你的项目中并且保持你的提交相对独立。
开始使用子模块
git clone https://github.com/zhoulujun/zhoulujun.cn-phpcms.git zhoulujun
cd zhoulujun
git submodule add tools
git submodule add https://github.com/zhoulujun/zhoulujun.cn-tools-vue.git tools-vue
添加子模块后运行git status, 可以看到目录有增加1个文件.gitmodules, 这个文件用来保存子模块的信息。
$ git status
On branch master Initial commit Changes to be committed:
(use "git rm --cached <file>..." to unstage) new file: .gitmodules
new file: assets
使用git init --bare在本地创建两个裸仓库,分别表示主仓库和依赖的子仓库,我们将主仓库命名为main,依赖的子仓库命名为lib, git subtree使用同样的初始化方法,下文不再赘述。
git submodule常用命令
查看子模块:git submodule
更新子模块:
更新项目内子模块到最新版本:git submodule update
更新子模块为远程项目的最新版本:git submodule update --remote
克隆包含子模块的项目:
克隆父项目:git clone https://github.com/demo.git assets
初始化子模块:git submodule init
更新子模块:git submodule update
递归克隆整个项目submodule:git clone https://github.com/demo.git assets --recursive
递归更新整个项目submodule:git submodule foreach git pull
删除子模块:git rm --cached subModulesA rm -rf subModulesA
--recursive表示递归地克隆git_parent依赖的所有子版本库。
git subtree(子树合并)
上面介绍的git submodule是Git自带的原生功能,我们接下来将要介绍的git subtree则是由第三方开发者贡献的contrib script,Git本身并不提供git subtree命令,contrib中包含一些实验性的第三方工具,由各自的作者进行维护。
同时这也让我们认识到git subtree不是Git原生支持的命令,而是第三方开发者通过Git的底层命令写出的一个高层次脚本,所以它是可以由基础的Git命令来实现的。
subtree与submodule的作用是一样的,但是subtree出现得比submodule晚,它的出现是为了弥补submodule存在的问题:
submodule不能在父版本库中修改子版本库的代码,只能在子版本库中修改,是单向的;
submodule没有直接删除子版本库的功能;
subtree则可以实现双向数据修改。官方推荐使用subtree替代submodule。
这里就先不提了
使用Git subtree命令
创建本地目录
语法:`git remote add <子仓库名> <子仓库地址>`
实例:`git remote add component git@xxx.git`
添加远程仓库(本地存在文件目录)
语法:`git remote add -f <子仓库名> <子仓库地址>`
实例:`git remote add -f component git@xxx.git`
使用 ( pull & push )
pull:`git subtree pull --prefix=component component master --squash`
push:`git subtree push --prefix=component component master --squash`
注意:**必须在 `component` 的父级目录执行**,使用起来还不是很方便。
submodule可以一起clone出来,只需添加--recursive递归参数就可以了,而subtree并不行,只能手动添加
个人使用submodule习惯了,所以天然觉得subtree复杂难用===难道就像用惯了intelliJ,就没有心思去用VScode了,哈哈!
有人对 submodule 和 subtree 的区别做的一个总结还是挺形象的: submodule is link; subtree is copy 。
更多的推荐阅读: Git应用详解第十讲:Git子库:submodule与subtree https://blog.csdn.net/qq_43425487/article/details/105632114
Git的子仓库原理分析
如果不是很了解底层原理,很可能会导致使用子仓库出现云里雾里的现象,搞不清楚是父级仓库先提交,还是子仓库先提交
git submodule原理分析
我们知道Git底层大致依赖了四种对象,构成了Git对于文件内容追踪的基础:
blob: 二进制大文件,可以通俗理解为对文件的修改
tree: 记录了blob对象和其他tree对象的修改,通俗理解为目录
commit: 提交对象,记录了本次提交的tree对象和父类的commit对象以及我们的提交信息
tag: 我们对当前提交记录版本的对象
更加详细的内容请参考《深入理解Git》
我们此处需要依赖一个print_all_object的工具函数,它会帮助我们将git仓库下的这四种对象按照反向提交历史的排序展现出来,可以将它放在环境变量下方便全局使用:
#!/bin/bash print_all_object() {
for object in `git rev-list --objects --all | cut -d ' ' -f 1`; do
echo 'SHA1: ' $object
git cat-file -p $object
echo '-------------------------------'
done
} print_all_object
我们在main仓库下执行print_all_object:
# 此时处于我们刚对子模块提交的那个时间点
# 对部分长的hash进行了截取处理,不影响阅读观感
print_all_object SHA1: a1cfd26e
tree c77ba9c2
parent ab118b8 feat: 增加子仓库依赖
-------------------------------
SHA1: ab118b8
tree f5771cd feat: 父级仓库创建index.js
-------------------------------
SHA1: c77ba9c2
100644 blob d8c9fb4 .gitmodules
100644 blob ddd81ae index.js
160000 commit 40f8536 lib
-------------------------------
SHA1: d8c9fb4
[submodule "lib"]
path = lib
url = /path/to/repos/lib.git
-------------------------------
SHA1: ddd81ae
console.log('main');-------------------------------
SHA1: f5771cd
100644 blob ddd81ae index.js
-------------------------------
index.js文件是blob对象,对应的file mode是100644,但是对于lib子仓库的确是一个commit对象, file mode为160000,这是Git中一种特殊的模式,表明我们是将一次提交的commit记录在Git当中,而非将它记录成一个子目录或者文件。
而这正式git submodule的核心原理,Git在处理submodule引用的时候,并不会去扫描子仓库下的文件的变化,而是取子仓库当前的HEAD指向的commit的hash值,当我们对子仓库进行了更改后,Git获取到子模块的commit值发生变化,从而记录了这个Git指针的变化。
在暂存区所以我们才发现了new commits这种提示语,Git并不关心子模块的文件如何变化,我只需要在当前提交中记录子模块的commit的hash值即可,之后我们从父级仓库拉取子仓库的时候,Git拉取了本次提交记录中的子模块的hash值对应的提交,就还原了我们的整个仓库的代码。
git submodule注意点
虽然使用git submodule为我们的开发带来了很多便利,但是随之而来也会导致一些比较容易犯的错误,整理出来,防止大家采坑:
当子模块有提交的时候,没有push到远程仓库, 父级引用子模块的commit更新,并提交到远程仓库, 当别人拉取代码的时候就会报出子模块的commit不存在 fatal: reference isn’t a tree。
如果你仅仅引用了别人的子模块的游离分支,然后在主仓库修改了子仓库的代码,之后使用git submodule update拉取了最新代码,那么你在子仓库游离分支做出的修改会被覆盖掉。
我们假设你一开始在主仓库并没有采用子模块的开发方式,而是在另外的开发分支使用了子仓库,那么当你从开发分支切回到没有采用子模块的分支的时候,子模块的目录并不会被Git自动删除,而是需要你手动的删除了。
参考文章:
Git submodule 子模块的管理和使用 https://www.jianshu.com/p/9000cd49822c
使用Git Submodule管理子模块 https://segmentfault.com/a/1190000003076028
Git子仓库深入浅出 https://juejin.im/post/6844904034722119694
子模块 http://gitbook.liuhui998.com/6_2.html
使用git subtree & submodule管理多个子项目 https://www.jianshu.com/p/84e34ac318e4
Git应用详解第十讲:Git子库:submodule与subtree https://blog.csdn.net/qq_43425487/article/details/105632114
git submoudle vs git subtree https://efe.baidu.com/blog/git-submodule-vs-git-subtree/
转载本站文章《Git 工具 - 子模块: submodule与subtree的使用》,
请注明出处:https://www.zhoulujun.cn/html/tools/VCS/git/8566.html
Git 工具 - 子模块: submodule与subtree的使用的更多相关文章
- git 工具 - 子模块(submodule)
From: https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97 子模块 有种情况我们经常 ...
- Git 工具 - 子模块
子模块 有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你独立开发的,用于多个父项目的库. 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使 ...
- 将git仓库从submodule转换为subtree
三个脚本 Alexander Mikhailian cat .gitmodules |while read i do if [[ $i == \[submodule* ]]; then mpath=$ ...
- Git应用详解第十讲:Git子库:submodule与subtree.md
前言 前情提要:Git应用详解第九讲:Git cherry-pick与Git rebase 一个中大型项目往往会依赖几个模块,git提供了子库的概念.可以将这些子模块存放在不同的仓库中,通过submo ...
- Git 子模块 - submodule
有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你 独立开发的,用于多个父项目的库. 现在问题来了:你想要把它们当做两个独立的项目,同时又想在 一个项目中使用另 ...
- Git 子模块 - submodule(转)
原文地址: http://www.cnblogs.com/kelsen/p/5918672.html 有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你 独立开 ...
- git子模块submodule
添加submodule: git submodule add 子模块git地址 把这个module放置的文件夹(这个文件夹须事先不存在) git submodule add http://xxx.x ...
- Git详解之六 Git工具(转)
Git 工具 现在,你已经学习了管理或者维护 Git 仓库,实现代码控制所需的大多数日常命令和工作流程.你已经完成了跟踪和提交文件的基本任务,并且发挥了暂存区和轻量级的特性分支及合并的威力. 接下来你 ...
- Git详解之六:Git工具
Git 工具 现在,你已经学习了管理或者维护 Git 仓库,实现代码控制所需的大多数日常命令和工作流程.你已经完成了跟踪和提交文件的基本任务,并且发挥了暂存区和轻量级的特性分支及合并的威力.(伯乐在线 ...
- Git详解之六 Git工具
以下内容转载自:http://www.open-open.com/lib/view/open1328070367499.html Git 工具 现在,你已经学习了管理或者维护 Git 仓库,实现代码控 ...
随机推荐
- 2023华为杯·第二届中国研究生网络安全创新大赛初赛复盘 Writeup
A_Small_Secret 题目压缩包中有个提示和另一个压缩包On_Zen_with_Buddhism.zip,提示内容如下: 除了base64还有什么编码 MFZWIYLEMFSA==== asd ...
- JAVA多线程(1)——线程
1.定义:线程是一个程序里面不同的执行路径 例子1:只有一个执行路径 (一个分支,即主线程)
- 标子查询优化和改写SQL案例
京华开发一哥们找我优化条报表SQL,反馈执行时间很慢需要 18s 才能出结果,安排. -- 原SQL SELECT 2 AS TYPE, to_char(a."create_time&quo ...
- 题解 CF1742G
题目描述: 给你一个序列 \(A\),要求将 \(A\) 重新排序,使得序列 \(A\) 的前缀或和序列 \(B\) 的字典序最大. 题目分析: 这道题我们首先考虑一个性质,就是前缀或和序列 \(B\ ...
- Video教程介绍(开篇)
教程简介 本文将简单描述视频网站教程,视频网站是一个类似于腾讯视频一样的网站,视频资源用户自己上传,然后提供友好的界面查看视频和搜索视频,并且提供管理页面对于视频进行管理,我们将使用Blazor作为前 ...
- 基于Python下MySQL数据库驱动
由于MySQL服务器以独立的进程运行,并通过网络对外服务,所以,需要支持Python的MySQL驱动来连接到MySQL服务器. 1.mysql-connector-python mysql-con ...
- Kafka 如何保证消息消费的全局顺序性
哈喽大家好,我是咸鱼 今天我们继续来讲一讲 Kafka 当有消息被生产出来的时候,如果没有指定分区或者指定 key ,那么消费会按照[轮询]的方式均匀地分配到所有可用分区中,但不一定按照分区顺序来分配 ...
- 现代统计分析软件Datainside在学生成绩分析中的应用
Datainside(薪火数据)是一款非常常用的统计分析软件,广泛应用于学术研究和商业领域. 在学生成绩分析方面,Datainside提供了丰富的功能和工具,可以帮助教育机构和研究人员深入理解学生的学 ...
- Go语言数组与切片学习总结
一.数组 数组的定义:相同类型的数据集合 go语言中数组的索引从0开始 没有赋值的数值型数组,默认值为0 数组一旦被创建,它的大小就是不可改变的 (1)声明数组与打印 var 变量名 [大小]变量类型 ...
- Unicode编码解码
一.Unicode概述 Unicode是一种字符编码标准,旨在解决不同字符集之间的兼容性问题.它为全球所有语言提供了一种统一的编码方式,使得各种字符能够在计算机系统中正确显示和处理.Unicode字符 ...