Better Practice in Git Submodule

(文中提到的仓库特指git仓库)

1. 背景介绍

开发中可能会遇到这样的情况:

  • 项目依赖一个library
  • 这个library在多个项目中都要用到(符合封装复用的原则)
  • 我需要根据需求修改library的内容
  • 不同项目依赖的library可能是不同版本

如果把library的文件直接放在当前项目的仓库中,会导致一个问题:不同项目同步library的更新会变成一场灾难(耗费大量时间且易出错)。

于是,submodule 诞生了:

submodule提供了一种能力:在一个仓库中,允许以“子目录”的形式存放另一个仓库,同时两个仓库独立提交。

(“子目录”打引号是因为并不是真正的子目录)

回到上面的需求:

  • library作为单独的仓库以submodule的形式引入到不同的项目中
  • 一个项目更新了library,其他项目可以方便地更新
  • 不同的项目可以方便地维护不同的library版本

...

2. 各个场景的用法

2.1 给仓库添加submodule

git submodule add url moduleName
添加submodule

2.2 clone一个有submodule的仓库

2.2.1常规操作

如果clone的仓库有submodule,是不能直接使用的,需要运行以下两条命令来初始化。

git submodule init
git submodule update
初始化有submodule的仓库

2.2.2 submodule中嵌套了submodule

上面的操作不会初始化嵌套的submodule,如果遇到嵌套的情况,你当然可以cd到对应目录,再次执行git submodule init和git submodule update,还有一个便捷操作:

git submodule update --init --recursive

2.3 更新远程submodule

submodule会经常更新, update 是submodule非常重要的操作,它的作用是:

Update the registered submodules to match what the superproject expects by cloning missing submodules, fetching missing commits in submodules and updating the working tree of the submodules.

可见,根据命令的参数,update的功能非常丰富:

  • clone本地缺失的submodule
  • fetch(submodule中)本地缺失的commits
  • 更新工作空间(就是当前目录中看到的代码)

更新远程submodule的基础操作是

git submodule update --remote

以下是各种场景的用法

2.3.1 远程添加了新的submodule

git submodule update --init --remote

2.3.2 远程submodule有更新,本地没更新

git submodule update --remote

2.3.3 远程submodule有更新,本地也有更新

现在是这篇文章最重要的部分,因为这个场景是submodule常见场景中最让人困惑的,如果你直接按照上面的操作(git submodule update --remote), 会发现本地的提交不见了。

想要解决这个问题,就要知道git submodule update --remote到底做了什么:

  • 对于每一个submodule, git 会fetch其远程master分支(*可配置)的最新commit。
  • 拉取到新的commit后,checkout到最新的commit(此时HEAD指针未指向任何分支,即submodule处于游离状态-detached)

综上,默认状态下,所有的submodule都处于游离状态, 新增本地提交后也是游离状态,这时执行git submodule update --remote,submodule被checkout到远程master分支(*可配置)最新的commit, 于是本地的提交从工作空间中消失了(如下图)。

常见问题之: 游离状态

了解到原因,就知道如何解决更新submodule时的游离问题以及本地修改丢失的问题

(1) 给submodule 切分支
cd到submodule的目录下,手动checkout到具体的分支下
(2) update时添加 --merge或--rebase
git submodule update --remote --merge
正确操作
Tip1: 关于配置submodule的分支

操作git submodule update --remote fetch的分支是可以配置的(默认是master),配置方式是:

git config .gitmodules submodule.<submoduleName>.branch <branch>

也可以改直接改.gitmodules这个配置文件

# in file .gitmodules
[submodule "module/D"]
    path = module/D
    url = git@xxx.git
    branch = test//改成对应的分支名 
Tip2: 别慌,你的东西丢不了

刚接触submodule的时候,很多朋友看到自己的本地提交丢失就慌了,因为各个记录中好像都找不到刚才的内容。这里介绍一个利器: reflog(顾名思义: ref的log,因为git中的HEAD以及分支都是ref指针,我们通过插件HEAD的log就能知道刚刚“丢失”的commit)

# cd to submodule directory
git reflog HEAD
reflog 示例

3. 一些好用的设置项

3.1 push仓库时,确保修改的submodule已经push

团队协作时,有一种很常见的错误,自己提交仓库时,忘了提交submodule的commit,导致同事无法更新到submodule最新的内容(因为它只在你的本地仓库)。为了避免这种错误,可以在push时添加git push --recurse-submodules=check帮助检查:

git push --recurse-submodules=check

或者修改配置,一劳永逸。

git config push.recurseSubmodules check

3.2 更好看的设置项

默认的设置项,看不到submodule修改的内容,可以修改配置:

git config status.submodulesummary 1
更友好的status

3.3 这些命令也太长了吧

上文中最重要的命令应该是git submodule update --remote了,本来就挺长的,在加一个--merge--rebase就更长了,这也太长了吧,手都敲酸了。。。别急,git alias(git别名机制)来了:

git config alias.supdate 'submodule update --remote --merge'

这样设置后就可以用git supdate代替git submodule update --remote --merge操作了。

4. 一些需要注意的东西

4.1 对于模型的理解

  • 虽然工作空间中看到的submodule是一个个子目录,但submodule不是以子目录的形式存放在主仓库中的(这也是前文“子目录”加引号的原因),这一点比较违反直觉。
  • 在底层的存储上,所有的submodule也是存在主仓库的.git目录中。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Add & Commit git init 初始化一个 Git 仓库(repository),即把当前所在目录变成...
    冬絮阅读 4,787评论 0 8
  • git教程 git简介 git是一个开源的分布式版本控制系统,用于敏捷高效的处理任何大小的项目。 git工作流程相...
    阿狸404阅读 276评论 0 0
  • 对于一个新的包括submodule的项目调用以下命令即可 或者 配置 首先是配置帐号信息 ssh -T git@g...
    brownfeng阅读 467评论 0 0
  • 分布式版本管理工具 git属于分布式 svn集中式 git安装 git初始化一个仓库 其实就是创建了一个.git隐...
    SnowDragonYY阅读 1,498评论 0 0
  • Git Tools - Submodules 1. 应用场景 需求 当你在一个项目 project1 上工作时,你...
    最好时光萌萌哒阅读 509评论 1 2