git日常使用中,基本都是一个项目一个Git仓库的形式,那么当我们的代码中碰到了业务级别的需要复用的代码,我们一般怎么做呢?

比如:某个工作中的项目需要包含并使用另一个项目。 也许是第三方库,或者你独立开发的,用于多个父项目的库。

所以需要提取一个公共的类库提供给多个项目使用,但是这个library怎么和git在一起方便管理呢?

现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。

我们大致的考虑一下,一般有两种方案:

  1. 抽象成NPM包进行复用;

  2. 使用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两种子仓库使用方案

  1. git submodule

  2. 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存在的问题:

  1. submodule不能在父版本库中修改子版本库的代码,只能在子版本库中修改,是单向的;

  2. 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为我们的开发带来了很多便利,但是随之而来也会导致一些比较容易犯的错误,整理出来,防止大家采坑:

  1. 当子模块有提交的时候,没有push到远程仓库, 父级引用子模块的commit更新,并提交到远程仓库, 当别人拉取代码的时候就会报出子模块的commit不存在 fatal: reference isn’t a tree。

  2. 如果你仅仅引用了别人的子模块的游离分支,然后在主仓库修改了子仓库的代码,之后使用git submodule update拉取了最新代码,那么你在子仓库游离分支做出的修改会被覆盖掉

  3. 我们假设你一开始在主仓库并没有采用子模块的开发方式,而是在另外的开发分支使用了子仓库,那么当你从开发分支切回到没有采用子模块的分支的时候,子模块的目录并不会被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的使用的更多相关文章

  1. 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 子模块 有种情况我们经常 ...

  2. Git 工具 - 子模块

    子模块 有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你独立开发的,用于多个父项目的库. 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使 ...

  3. 将git仓库从submodule转换为subtree

    三个脚本 Alexander Mikhailian cat .gitmodules |while read i do if [[ $i == \[submodule* ]]; then mpath=$ ...

  4. Git应用详解第十讲:Git子库:submodule与subtree.md

    前言 前情提要:Git应用详解第九讲:Git cherry-pick与Git rebase 一个中大型项目往往会依赖几个模块,git提供了子库的概念.可以将这些子模块存放在不同的仓库中,通过submo ...

  5. Git 子模块 - submodule

    有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你 独立开发的,用于多个父项目的库. 现在问题来了:你想要把它们当做两个独立的项目,同时又想在 一个项目中使用另 ...

  6. Git 子模块 - submodule(转)

    原文地址: http://www.cnblogs.com/kelsen/p/5918672.html 有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你 独立开 ...

  7. git子模块submodule

    添加submodule: git submodule add 子模块git地址  把这个module放置的文件夹(这个文件夹须事先不存在) git submodule add http://xxx.x ...

  8. Git详解之六 Git工具(转)

    Git 工具 现在,你已经学习了管理或者维护 Git 仓库,实现代码控制所需的大多数日常命令和工作流程.你已经完成了跟踪和提交文件的基本任务,并且发挥了暂存区和轻量级的特性分支及合并的威力. 接下来你 ...

  9. Git详解之六:Git工具

    Git 工具 现在,你已经学习了管理或者维护 Git 仓库,实现代码控制所需的大多数日常命令和工作流程.你已经完成了跟踪和提交文件的基本任务,并且发挥了暂存区和轻量级的特性分支及合并的威力.(伯乐在线 ...

  10. Git详解之六 Git工具

    以下内容转载自:http://www.open-open.com/lib/view/open1328070367499.html Git 工具 现在,你已经学习了管理或者维护 Git 仓库,实现代码控 ...

随机推荐

  1. Unity - UIWidgets 5. Redux接入(一) 基本接入

    对响应式的数据组织结构不太熟, 直接尝试Redux走起 参考资料 Redux的基本概念 state 一个字段用于存储状态 存储state的地方称为"store", 类似Model\ ...

  2. 每天5分钟复习OpenStack(七)内存虚拟化

    标题中的存储虚拟化,涉及到两个方面,分别是内存和磁盘的虚拟化技术.内存的虚拟化就不得不提EPT和VPID 技术. 首先申明下本人其实不想写一些纯理论的东西,但是架不住面试经被问,为此特将一些特别复杂的 ...

  3. JavaScript:对象的三个属性

    每一个对象都有与之相关的原型(prototype).类(class)和可扩展性(extension attribute). 原型 prototype 对象的原型属性是用来继承属性的.通过对象直接量创建 ...

  4. Redis7

    Redis是基于内存的K-V键值对内存数据库 浅谈Redis7新特性 主要是自身底层性能和资源利用率上的提高和优化. 多AOF文件支持 config命令增强 限制客户端内存使用 listpack紧凑列 ...

  5. 聊聊 GPU 产品选型那些事

    随着人工智能的飞速崛起,随之而来的是算力需求的指数级增加,CPU 已经不足以满足深度学习.大模型计算等场景的海量数据处理需求.GPU 作为一种强大的计算工具,无论是高性能计算.图形渲染还是机器学习领域 ...

  6. (数据科学学习手札155)基于martin为在线地图构建字体切片服务

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,在之前的一篇文章(基于mart ...

  7. IDEA编译器的永久试用设置与基本使用

    参考视频: 最通俗易懂的JDK.IDEA的安装使用权威指南 2023新版前端Web开发HTML5+CSS3+移动web视频教程,前端web入门首选黑马程序员 一.安装包下载与安装 官网下载地址 个人使 ...

  8. [USACO2007NOVG] Sunscreen

    题目描述 To avoid unsightly burns while tanning, each of the$ C (1 ≤ C ≤ 2500) $cows must cover her hide ...

  9. vue-test --------事件修饰符

    <template> <h3>事件修饰符</h3> <a @click="clickHandle" href="www.baid ...

  10. ubuntu防火墙ufw基本使用方法

    ubuntu系统基本使用方法 防火墙 # 查看服务器防火墙状态 ufw status # 将防火墙设置为可用状态 ufw enable # 将防火墙设置为关闭状态 ufw disbale # 放行端口 ...