您的当前位置:首页正文

Git子模块

2024-11-30 来源:个人技术集锦

简介

git subModule

1、拉取子模块仓库
  • git submodule add <repository_url> <submodule_path>
  • 会拉取配置git仓库的默认分支和merged状态的最新commit,并克隆在主模块的指定目录中,(为了保证子模块的可追述性和稳定性,git默认拉取远程仓库的merge状态下最新的commit,不支持直接配置commit hash来获取指定提交,可以在主模块手动chekout到指定commit)
  • 添加引用,会将克隆基于的commit hash、URL、Branch等配置信息记录在主模块的.gitmodule文件(新建)中以及主模块的索引.git文件夹中记录当前子模块的 commit hash。

.gitmodule文件:

  • .gitmodules: 主要用于描述项目中的子模块信息,并且这个文件是版本化的,会根据主模块提交到远程。因此,当其他人克隆主模块时,他们会得到相同的 .gitmodules 文件内容,可以正确地初始化子模块。
  • .git/config: 包含子模块的本地配置,主要用于本地环境的配置。每次你初始化或add更新子模块时,这个文件会被自动更新。

执行git ls-tree HEAD查看克隆的子模块文件夹会显示类似这样的信息:
160000 commit d6032c3e83106319e5410738055071954193724a common-web

  • 各部分含义
2、开发之后提交代码

由于子模块是独立的git仓库,所以每次更新子模块之后要回到主模块,通过git add .来更新主模块对子模块的引用,然后通过commit、push操作,将子模块记录跟着主模块提交到远程,以便其他人拉取主模块时也能获取正确的子模块内容
git add: 如果子模块有更改,会在本地.git配置中更新子模块的应用commit hash…
git commit: 会将子模块的引入和主模块一起commit,即占位的空白文件夹。

3、克隆主模块

克隆主模块之后,子模块是一个空白文件夹,commit类型的引用链接。需要init、update拉取子模块代码

  • git submodule init: 读取commit hash和.gitmodule文件信息,更新主模块中.git配置文件,以便知道在哪里拉取子模块代码
  • git submodule update:根据本地.git配置拉取子模块的特定commit。
4、总结

不管在主模块还是在子模块,只要子模块有更新,主模块都要更新子模块的引用才能获取到最新的代码,所以使用git submodule来进行多模块开发进行子模块更新时,一般需要两步:

  1. 在子模块 B 中进行更改和提交:
    • 进入子模块 B 的目录。
    • 进行代码更改、添加文件到暂存区,并提交到子模块 B 的仓库。
    • 这些提交只会影响子模块 B 自身,不会自动更新主项目 A 中的子模块引用。
  2. 在主项目 A 中更新子模块 B 的引用:
    • 进入主项目 A 的根目录。
    • 更新子模块的引用,使其指向子模块 B 的最新提交。
    • 将这个子模块引用的更改提交到主项目 A 的仓库。

整体流程图:

PS: 每次更新子模块都要进入子模块,然后回到主模块在更新引用,很麻烦。

5、常用指令合集
// 子模块初始化
git submodule init
// 子模块更新
git submodule update
// 更新某个子模块代码
git submodule update --remote path/to/submodule
// 更新主模块下所有子模块代码
git submodule update --remote
// 子模块更新之后,同步主模块更新子模块的引用commit hash
git add .
// 递归克隆整个项目submodule
// --recursive表示递归地克隆git_parent依赖的所有子版本库。
git clone https://github.com/demo.git assets --recursive 
// 递归更新整个项目submodule
git submodule foreach git pull

git subtree

1、问题以及解决
  • 使用工具过滤历史记录: 可以使用 Git 提供的工具(如 git log --submodule 或者 git log --grep=)来过滤和查看主模块与子模块的提交记录,避免混淆。
  • 分支和标签的策略性使用: 在主模块中,可以使用分支和标签来标识子模块合并的时间点和版本。以便于更好地追踪和管理子模块的合并历史。
2、指令合集
// 主模块引入子模块
// --prefix 指定了子项目 B 应该放在主项目 A 中的哪个子目录。
// --squash 将子项目 B 的所有历史记录合并为一个提交,以减少主项目 A 的历史记录复杂性。
git subtree add --prefix=path/to/moduleB https://example.com/repoB.git main --squash
// 提交子模块代码
git subtree push --prefix=path/to/moduleB <remote-url> <branch>
// 拉取子模块代码
git subtree pull --prefix=path/to/moduleB <remote-url> <branch>
// 更新主模块下所有子模块代码
git submodule update --remote

git submodule和git subtree的区别

  • Git Submodule:
    • 子模块的代码独立存在,主模块只记录子模块的特定版本(commit hash)。
    • 推送主模块时,不包含子模块的实际代码,只推送子模块的引用更新。
    • 子模块的实际代码变动需要单独推送到子模块的远程仓库。
  • Git Subtree:
    • 子模块的代码整合到主模块中,成为主模块代码的一部分。
    • 推送主模块时,子模块的代码和主模块的代码一起推送到远程仓库。
    • 子模块的历史记录和变动直接体现在主模块的提交中。

简单理解为:submodule is link(commit hash), subtree is copy

显示全文