git submodule

2023-05-16

此文已由作者张磊薪授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

前言

submodule 目前对 git 仓库拆分的已有实现之一。环境 git version 2.7.4.windows.1

准备工作

  1. 首先创建主仓库 subrepo-master,随意提交一次文本,接着拉取到本地

  2. 建立子仓库 subrepo 和 subrepo1,随意提交一次文本

操作

  1. 在主仓库下运行如下命令后,可以看到在仓库中多出来文件 subrepo 以及 .gitmodules

    
     git submodule add [subrepo url]  
  2. 运行 git status

    
     On branch master
     Changes to be committed:
     (use "git reset HEAD <file>..." to unstage)         new file:   .gitmodules         new file:   subrepo  
  3. 接着添加 submodule1,并指定路径

    
     git submodule add [subrepo1 url] ./module/module1  
  4. 运行 git status 得到

    
     On branch master
     Changes to be committed:
     (use "git reset HEAD <file>..." to unstage)         new file:   .gitmodules         new file:   module/module1         new file:   subrepo  

    cat .gitmodules 得到

    
     [submodule "subrepo"]
             path = subrepo
             url = [subrepo url]
     [submodule "module/module1"]
             path = module/module1
             url = [subrepo1 url]  

    这是一份子模块与路径的映射关系图,这份文件很重要,git 根据这份文件去识别 submodule,所以这份文件应该被加入版本控制

  5. 接着运行提交命令,可以看到三个目录都被添加到仓库了,注意子模块下面的文件并没有被添加进去。160000 的含义是这是 Git 中的一种特殊模式,基本上意味着您将提交记录为目录条目而不是子目录或文件。 然后提交到远端,就有了一个 submodule 的仓库 :)

    
     $ git commit -m "add submodule"
     [master 5c88033] add submodule 3 files changed, 8 insertions(+) create mode 100644 .gitmodules create mode 160000 module/module1 create mode 160000 subrepo  
  6. 接下来模拟多人协作,首先新建一个文件夹,运行命令后,会发现 subrepo 以及 module/module1 目录并没有文件。

    
     git clone [subrepo-master url]  
  7. 这时候需要运行命令 git submodule init 去初始化本地配置文件以及 git submodule update 拉取代码。

    
     $ git submodule init
     Submodule 'module/module1' (https://github.com/xxx/subrepo1.git) registered for path 'module/module1'
     Submodule 'subrepo' (https://github.com/xxx/subrepo.git) registered for path 'subrepo'  
    
     $ git submodule update
     Cloning into 'module/module1'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
     Unpacking objects: 100% (3/3), done.
     Checking connectivity... done.
     Submodule path 'module/module1': checked out '5c47ee69895b8acd3291eb0551f751ba43  488c68'
     Cloning into 'subrepo'... remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
     Unpacking objects: 100% (3/3), done.
     Checking connectivity... done.
     Submodule path 'subrepo': checked out '1b6f8742270c5affc9fc055ce1bf03907cf9a0f8'  
  8. 通过 6、7 两步完成还是太过麻烦,好在有简单的命令,在 clone 那一步运行 git clone --recursive 就会自动完成 6、7两步。

  9. 如果只更改主仓库的代码,那各种操作大家都会。下面主要是子仓库远端修改、子仓库改分支、子仓库本地修改推送远端、主仓库下的子仓库的引用同时被多人修改等等。

  10. 子仓库远端修改

    修改子仓库远端的内容,接着同步到本地。一种方式,进入子仓库目录 git fetch && git merge,另一种方式 git submodule update --remote [子仓库目录]。 运行 git diff,可以看到

    
    $ git diff
    diff --git a/module/module1 b/module/module1index 5c47ee6..e55e7ef 160000--- a/module/module1+++ b/module/module1
    @@ -1 +1 @@
    -Subproject commit 5c47ee69895b8acd3291eb0551f751ba43488c68
    +Subproject commit e55e7efb44f1c07d23e2deede0a13fa8a953f96d
    diff --git a/subrepo b/subrepoindex 1b6f874..afdc5bf 160000--- a/subrepo+++ b/subrepo
    @@ -1 +1 @@
    -Subproject commit 1b6f8742270c5affc9fc055ce1bf03907cf9a0f8
    +Subproject commit afdc5bf559beacb08032e23d22a2beaa65d3ca9c  
  11. 子仓库改分支

    在子仓库远端新建分支 a,然后在主仓库运行命令,再运行 cat .gitmodules 查看,发现 subrepo 多了 branch 指向。运行 git submodule status 可以看到 submodule 的状态。运行 git log -p --submodule 可以查看到子模块的日志修改。同时可以将 branch 指向 tag ,但不支持 commitid。这里有一点,子模块记录的是 commitid 即使指定了 branch,同时它不会主动升级(主动升级会导致每个人的代码不一致)。

    
    git config -f .gitmodules submodule.subrepo.branch a
    git submodule update --remote  
    
    $ cat .gitmodules
    [submodule "subrepo"]
            path = subrepo
            url = https://xx/subrepo.git
            branch = a
    [submodule "module/module1"]
            path = module/module1
            url = https://xx/subrepo1.git  
    
    $ git submodule status
    +e55e7efb44f1c07d23e2deede0a13fa8a953f96d module/module1 (remotes/origin/HEAD)+afdc5bf559beacb08032e23d22a2beaa65d3ca9c subrepo (remotes/origin/HEAD)  

    指向 commitid 的方案是运行如下指令,通过将这个子模块文件夹的 commitid 指定为新的,即可完成指定相应 commitid 的动作。此时观察 .gitmodules,会发现 subrepo 指向的 branch 字段消失。

    
    cd subrepo
    git checkout fa1317acd ..
    git add subrepo
    git commit ...  

    其他人在各自目录运行,即可获取最新更新

    
    git submodule update  
  12. 子仓库本地修改推送远端

子模块本地提交代码

当本地子模块没有指定 branch 的时候,是处于一个称作 “游离的 HEAD”(detached HEAD) 的状态(git 提示的是 commitid 而不是分支名)。这个状态下你可以正常的 git 操作,但是此时是没有分支进行跟踪的,也就没办法推送代码。想摆脱这种状态,在子模块运行 git checkout branch 即可。

子模块拉取服务端代码

然后对远端子模块做一次修改(称为 patchA),并运行 git submodule update --remote --merge,就可以看到 subrepo 发生了改变,此时可以提交这个改变,即将 subrepo 指向的旧的 commitid 换成  patchA 的 commitid。后面的人更新代码的时候会拿到新的 commitid,如果没有人运行 update 命令并提交,则所有人拿到的都是旧的代码。


  $ git status
  On branch master
  Your branch is up-to-date with 'origin/master'.
  Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

          modified:   subrepo (new commits)  no changes added to commit (use "git add" and/or "git commit -a")  

本地提交与远端合并代码

接下来对本地的 subrepo 做一次提交,接着对远端的子模块也做一次提交。然后更新子模块。可能会运行 git submodule update --remote,此命令只会更新成远端的代码,会发现本地的代码丢失,此时不需要慌张,运行 git checkout branch 即可获取本地代码。合并的命令是 git submodule update --remote 后面加上 --merge 或者 --rebase 即可,会遇到冲突的情况,进入目录手工解决即可。运行 git diff -p --submodule,可以查看到子模块的修改,现在修改的内容包含本地提交以及远端合并的代码,接下来就需要进行推送。

本地提交到远端  在主仓库进行一次提交,提交子仓库最新的 commitid。

如果我们在主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到麻烦,因为他们无法得到依赖的子模块改动。那些改动只存在于我们本地的拷贝中。

为了确保这不会发生,你可以让 Git 在推送到主项目前检查所有子模块是否已推送。   git push --recurse-submodules=check (如果子模块没有提交,会直接报错)或者 git push --recurse-submodules=on-demand(如果子模块没有提交,会尝试提交,提交不成功同时会阻止主仓库的推送)

主仓库下的子仓库的引用同时被多人修改

比如 subrepo 指向的 commitid 是 a,A 成员修改为 b, B 成员修改为 c,这时候就需要去合并代码,让其指向最新的 a1。

如果 b 和 c 是祖先关系,则 git 会直接快进式合并。下面模拟分叉的情况。

检出一个本地仓库, git reset --HARD oldcommitid,然后将子模块的代码进行修改,接着添加到主仓库进行提交。接下来运行 git pull,会发现有冲突了。


$ git pull origin masterFrom https://xx/subrepo-master* branch            master     -> FETCH_HEADwarning: Failed to merge submodule subrepo (commits don't follow merge-base)
Auto-merging subrepo
CONFLICT (submodule): Merge conflict in subrepo
Automatic merge failed; fix conflicts and then commit the result.  

解决此问题的方案是,首先 git diff 查看一下代码


diff --cc subrepo
index 4660574,55167f5..0000000--- a/subrepo
+++ b/subrepo
diff --git a/module/module1 b/module/module1index e55e7ef..5c47ee6 160000--- a/module/module1+++ b/module/module1@@ -1 +1 @@
-Subproject commit e55e7efb44f1c07d23e2deede0a13fa8a953f96d
+Subproject commit 5c47ee69895b8acd3291eb0551f751ba43488c68  

找到冲突的 commitid(其中 4660574 是公有的, 55167f5 是上游的),进入子模块,然后检出为分支 git branch merge1 55167f5 && git merge merge1,如果有冲突,此时就需要手动解决冲突了,解决完成,提交。再回到主目录,查看是否有冲突,对主仓库进行提交。此时再 git pull 则无冲突,然后就可以用 git push --recurse-submodules=on-demand 提交本次修改。

删除子模块后会发现 .gitmodules 文件内容同时发生了改变。如果需要备份,请提前备份目录


git submodule deinit module/module1rm -rf .git/modules/modulegit rm -f module/module1vi .git/config  

技巧

  • foreach,可以在每一个子模块中运行任意命令。例如:保存进度, git submodule foreach 'git stash';切换分支 git submodule foreach 'git checkout -b featureA' 等等,这个可以对子模块统一进行操作管理。

  • git 命令别名,举例:

    
      $ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
      $ git config alias.spush 'push --recurse-submodules=on-demand'
      $ git config alias.supdate 'submodule update --remote --merge'  

这样运行更新命令的时候,就简化为 git supdate,其他同理。

  • git status 之类的获取的均是主仓库的状态,无法查看子模块的状态。建议 git config --global status.submoduleSummary true。git config --global diff.submodule log 可以查看到子模块的日志

缺点

  1. 例如在有子模块的项目中切换分支可能会造成麻烦。 如果你创建一个新分支,在其中添加一个子模块,之后切换到没有该子模块的分支上时,你仍然会有一个还未跟踪的子模块目录,这时候如果不小心提交了这个子模块(git commit -am "message"),就会有问题了。

  2. 对子模块的更新略显复杂,每次操作都需要所有人手动同步更新,增加了学习成本。

  3. 对子模块做了修改,需要先推送子模块再主模块,同时拉取的时候也需要先主模块,再子模块。

  4. 对子模块做本地修改需要先检出分支,否则有可能在 “游离的 HEAD” 上做修改。

  5. 删除子模块,需要的步骤有点复杂。

  6. 如果子模块被高频次更新,会有大量合并代码的工作,参考上面的 10-13

  7. 如果你的同事更新了 submodule,然后更新了父项目中依赖的版本号。你需要在 git pull 之后,调用 git submodule update 来更新 submodule 信息。这儿的坑在于,如果你 git pull 之后,忘记了调用 git submodule update,那么你极有可能再次把旧的submodule 依赖信息提交上去(使用 git submit -am "message" 或者 git add . 提交的人会遇到这种事)。

优点

  1. 不需要获取子模块整个代码库

  2. 主仓库只是获取到了子仓库的引用

参考

  1. https://git-scm.com/book/en/v2/Git-Tools-Submodules

  2. https://medium.com/@porteneuve/mastering-git-submodules-34c65e940407 最后的总结很好


免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击。


相关文章:
【推荐】 扫脸动画
【推荐】 网易云MongoDB分片集群(Sharding)服务已上线

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

git submodule 的相关文章

  • Github 版本如何生成存档文件名?

    我刚刚在 github 上为我的 NFQL 软件创建了版本 这是发布页面 https github com vbajpai nfql releases https github com vbajpai nfql releases 对于最新版
  • Git - 显示远程分支的远程名称

    是否有一个 Git 命令可以显示远程分支的远程名称 目前 我坚持使用 shell utils 从远程分支引用中提取远程名称 例如 echo remote name branch name sed r s 1 remote name 有时出于
  • .gitignore 文件夹内容[重复]

    这个问题在这里已经有答案了 可能的重复 在 Windows 上忽略 Git 存储库中的目录 https stackoverflow com questions 343646 ignoring directories in git repos
  • 恢复 git reset --soft

    我修改了我的分支中的一些文件并做了一个 git add all 但这添加了一些我不打算为提交添加的文件 所以我做了一个 git reset soft HEAD 2 instead of doing git reset HEAD 但之前的提交
  • 我可以从命令行向 github 添加问题吗?

    我是 git 新手 我希望能够通过 github 绘制我的项目的计划和进度 问题是 这需要在 github com 上使用浏览器进行大量点击 并且我希望能够通过使用命令行程序来自动执行该任务 github 有命令行界面吗 有一个ghi ge
  • 尽管有 svn 复制,如何 git svn 克隆完整历史记录

    在我的公司 我们即将从 svn 切换到 git 我们使用的 SVN 非常大 没有 svn 布局 并且在每个版本拆分上我们都制作了一个 svn 副本 SVN存储库结构 svnserver company de product xy 主要版本号
  • 如何损坏 Git 存储库?

    创建损坏的 git 存储库有哪些方法 有没有有趣的方法可以永久损坏 git 存储库 你能否削弱一个 git 存储库 使其行为有些正常 但会做一些奇怪的事情 我的兴趣来自于当有人担心他们是否真的创建了不可恢复的状态时 它通常很容易修复或至少可
  • 如何避免开发人员将凭证推送到 bitbucket?

    我们有一个应用程序 它使用 JSON 文件来定义数据库和其他 API 的连接字符串和凭据 理想情况下 这些凭证作为变量添加到 bitbucket 存储库中 并在 Jenkins 的构建期间进行替换 开发商在当地与他们合作时更换了他们 我们需
  • git 克隆错误:致命:git upload-pack:由于远程端可能的存储库损坏而中止

    我对 git 存储库具有读 写访问权限 但是当我尝试 git clone 时 出现以下错误 x ubuntu temp git clone email protected cdn cgi l email protection Corp ap
  • Git守护进程克隆错误

    All 我正在按照以下指示进行操作this SO https stackoverflow com a 377293 724357答案 快速提供回购 当我跑步时git clone git ipAddr git project我得到这个输出 r
  • Git 在推送代码时返回错误 403 [重复]

    这个问题在这里已经有答案了 一切都工作正常 直到我创建了一个新的 GitHub 帐户 当我尝试使用新帐户第一次将代码推送到 github 服务器时 出现以下错误 remote Permission to NEW USER NEW REPO
  • 通过链接进入 git-repo,无需冗长的对话框

    In a directory I have symbolic links into a git administered directory all under Linux Every time I want to e dit such a
  • RuntimeError:模型类 django_messages.models.Message 未声明显式 app_label 并且不在 INSTALLED_APPS 中的应用程序中

    我正在尝试使用https github com arneb django messages https github com arneb django messages打包我的消息传递内容并尝试了以下操作 pip install git h
  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • 从 github 中删除子项目提交

    我有两个存储库A and B 我错误地在我的机器上将仓库 B 克隆到了 A 内 我从存储库 B 中删除了所有代码 但是当我在源上从 A 推送并合并代码时 它还显示了子项目提交B在 Github 仓库上 我想从我的 master 上删除子项目
  • 在 Web 应用程序中显示最新的提交值?

    我有一些 Rails 应用程序 我使用 Git 作为版本控制系统 我使用 GitHub 或 Beanstalk 作为存储库主机 从理论上讲 我想要做的事情非常简单 以某种方式在 Web 应用程序的页脚中显示最新的提交 ID 号 哈希值 这样
  • gitlab 请求将分支 A 合并到开发中(落后 3 次提交)我应该担心吗?

    在 gitlab 中创建合并请求时 我经常收到一条消息 请求将分支 A 合并到开发中 x 提交落后 gitlab想告诉我什么 我应该担心还是需要修复某些东西 什么 一段时间后合并请求在项目中打开时 由于其他人合并了自己的更改 您尝试合并到的
  • 致命:不是 git 存储库(或任何父目录):.git [重复]

    这个问题在这里已经有答案了 当我尝试推送 github com 上的现有存储库时 当我输入命令时 网站提示我将其输入终端 我收到了以下致命错误消息 Not a git repository or any of the parent dire
  • git Blame:合并后正确的作者

    GIT 合并引入了新的提交 这会导致 git Blame 问题 合并的行似乎是由进行合并的开发人员提交的 我可以理解这种情况冲突的变化 因为他解决了冲突 但是有没有办法让非冲突线路不发生这种情况呢 一些 git Blame 的选择 如果没有
  • 致命:无法将 HEAD 解析为有效引用

    我正进入 状态fatal Failed to resolve HEAD as a valid ref 每当我尝试承诺时 我努力了 echo ref refs heads master gt git HEAD 但它不起作用 也尝试过 git

随机推荐

  • Git clone的使用方法

    使用Git clone项目 1 首先我们要确保我们的电脑上已经安装Git 桌面点击右键出现如下图所示的两个Git即Git已经安装 2 在电脑的任意一个磁盘里新建一个本地文件夹作为clone项目的保存文件夹 3 在码云上面打开我们要clone
  • error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“0”不匹配值“2”

    报错1 xff1a error LNK2038 检测到 ITERATOR DEBUG LEVEL 的不匹配项 值 0 不匹配值 2 解决 xff1a xff08 1 xff09 工程的模式和库的模式不一致 xff0c 工程为Debug模式
  • 关于p操作和v操作的理解

    操作系统之PV操作 今天在做操作系统老师布置的操作系统作业 xff0c 但是碰到了一个有关pv操作的问题 xff0c 由于对pv操作的理解不是很透彻 xff0c 所以我查阅了很多资料 xff0c 下面来简单的通俗的介绍一下pv操作 1 信号
  • 相机针孔模型----从世界坐标系,到相机坐标系,再到图像物理坐标系,最后到图像像素坐标系的转换过程解析

    看了很多讲解针孔相机模型中从世界坐标系 gt 到相机坐标系 gt 图像坐标系的文章 xff0c 心里的疑惑也逐渐展开 xff0c 现在总结一下自己的理解 xff1a 世界坐标系 相机坐标系 图像物理坐标系 图像像素坐标系在我的另一篇博文里已
  • 如何在cmd查看文件内容的MD5值

    在cmd下进入 要查看的文件目录 进入cmd xff0c 输入如下命令 xff1a certutil hashfile 文件名称 文件类型 MD5 结果显示如下 SHA1 的 test py 哈希 364ebe3569456ec7e77de
  • CMakeLists.txt文件

    举例说明 xff1a CMake 构建 HelloSlam 工程 1 Ctrl 43 Alt 43 T 按键打开终端 2 在选定路径下建立工程文件夹 xff1a mkdir HelloSlam 3 构建 HelloSlam 的文件目录结构
  • PX4 CMakeLists.txt分析

    简单的概述 make 和 cmake 是linux UNIX系统下广泛使用的构建编译规则工具 xff0c 面对复杂庞大的工程 xff0c 各种源文件和工具文件分布在工程目录下 xff0c 如何组织和有序地编译和使用这些文件 xff0c 显然
  • iOS OC消除黄色警告⚠️ (不断的更新中...)

    开发一个项目时 xff0c 难免会产生很多警告 xff0c 一些是第三方或是老代码不再被支持造成的 xff0c 但并不影响使用 xff0c 这些警告其实可以直接隐藏掉 xff01 还有一些 警告可能是系统方法弃用 不兼容指针类型 未使用变量
  • AUTOSAR E2E & SecOC Comparison

    AUTOSAR E2E amp SecOC Comparison 前面已经介绍过了E2E 和 SecOC CMAC 了 xff0c 既然2者都可以进行数据完整性保护 xff0c 那么2者有什么区别呢 下面基于我的经验所总结 欢迎补充 xff
  • 解决:source devel/setup.bash(只在当前终端生效)的问题,使其在其他终端

    解决方法 xff1a 终端输入 xff1a gedit bashrc 文件打开后直接翻到最后面 在底部添加source catkin ws devel setup bash 保存退出即可
  • ROS与C++入门教程

    https www ncnynl com archives 201701 1279 html
  • HiChart图表统计:jsp中hichart用法以及从后台获取数据

    在做web服务器时 xff0c 用到了图表 xff0c 对一天内资源的下载量进行统计 xff0c 让数据更加的直观 上网查了很多资料 xff0c 最后发现HiChart很好用 xff0c 相对比较简单 下面作以详细介绍 xff1a 1 首先
  • [论文]欠驱动水下机器人的平面轨迹规划与跟踪控制设计

    论文 欠驱动水下机器人的平面轨迹规划与跟踪控制设计 摘要 研究了欠驱动自主水下航行器在水平面上的轨迹规划与跟踪控制的组合问题 给定光滑的 惯性的二维参考轨迹 xff0c 规划算法利用车辆动力学计算参考方向和机体固定速度 利用这些 xff0c
  • sql查询语句汇总,先撸为敬

    一 简单查询语句 group by 和having的区别 链接 二 复杂查询 1 数据分组 max min avg sum count SQL gt SELECT MAX sal MIN age AVG sal SUM sal from e
  • 最优化的基本概念

    最优化的基本概念 连续和离散优化问题无约束和约束优化问题随机和确定性优化问题线性和非线性规划问题凸和非凸优化问题全局和局部最优解优化算法 一般来说 xff0c 最优化算法研究可以分为 xff1a 构造最优化模型 确定最优化问题的类型和设计算
  • [RISCV]为RISC-V移植FreeRTOS系列之一 -- 目录结构

    前言 写这篇文章的时候 xff0c 我基本已经完成了这项工作了 xff0c 花了一周的时间来把freertos porting到Andes公司的N25 riscv core上 xff0c 本来其实是想支持国产的RT Thread xff0c
  • [RISCV]为RISC-V移植FreeRTOS系列之三 -- 时基

    前言 书接上回 xff0c 上回说到我们已经做好了准备 xff0c 所谓万事具备 xff0c 就差一场东风 xff0c 而能吹动FreeRTOS这条大船的是什么呢 xff1f 没错 xff0c 聪明的你已经猜到了 xff0c 是时基 有过其
  • [RISCV]为RISC-V移植FreeRTOS系列之四 -- 中断与trap handler

    前言 上回说到了我们已经把系统的心跳动起来了 xff0c 但是这里面还有一个问题 xff0c 我们都知道timer中断 xff0c 中断的trap怎么来的呢 这回就来解决这个事情 作者 xff1a wangyijieonline 链接 xf
  • [RTOS]uCOS、FreeRTOS、RTThread、RTX等RTOS的对比之特点

    最近正好又重新回顾了一下这几款OS xff0c 心里一直有个疑问 xff0c 明明这几款RTOS是这么像 xff0c 为什么还要搞出这么多个来呢 xff0c 最后的结论就是 xff0c 管他呢 xff0c 反正哪个用的顺手用哪个 本篇博客就
  • git submodule

    此文已由作者张磊薪授权网易云社区发布 欢迎访问网易云社区 xff0c 了解更多网易技术产品运营经验 前言 submodule 目前对 git 仓库拆分的已有实现之一 环境 git version 2 7 4 windows 1 准备工作 首