(文中提到的倉庫特指git倉庫)
1. 背景介紹
開發(fā)中可能會遇到這樣的情況:
- 項目依賴一個library
- 這個library在多個項目中都要用到(符合封裝復用的原則)
- 我需要根據需求修改library的內容
- 不同項目依賴的library可能是不同版本
如果把library的文件直接放在當前項目的倉庫中暴凑,會導致一個問題:不同項目同步library的更新會變成一場災難(耗費大量時間且易出錯)晦譬。
于是,submodule 誕生了:
submodule提供了一種能力:在一個倉庫中娩井,允許以“子目錄”的形式存放另一個倉庫鳖昌,同時兩個倉庫獨立提交。
(“子目錄”打引號是因為并不是真正的子目錄)
回到上面的需求:
- library作為單獨的倉庫以submodule的形式引入到不同的項目中
- 一個項目更新了library,其他項目可以方便地更新
- 不同的項目可以方便地維護不同的library版本
...
2. 各個場景的用法
2.1 給倉庫添加submodule
git submodule add url moduleName
2.2 clone一個有submodule的倉庫
2.2.1常規(guī)操作
如果clone的倉庫有submodule缅刽,是不能直接使用的读跷,需要運行以下兩條命令來初始化梗搅。
git submodule init
git submodule update
2.2.2 submodule中嵌套了submodule
上面的操作不會初始化嵌套的submodule,如果遇到嵌套的情況效览,你當然可以cd到對應目錄无切,再次執(zhí)行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), 會發(fā)現本地的提交不見了。
想要解決這個問題听绳,就要知道git submodule update --remote到底做了什么:
- 對于每一個submodule, git 會fetch其遠程master分支(*可配置)的最新commit颂碘。
- 拉取到新的commit后,checkout到最新的commit(此時HEAD指針未指向任何分支椅挣,即submodule處于游離狀態(tài)-detached)
綜上头岔,默認狀態(tài)下,所有的submodule都處于游離狀態(tài), 新增本地提交后也是游離狀態(tài)贴妻,這時執(zhí)行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
3. 一些好用的設置項
3.1 push倉庫時锈嫩,確保修改的submodule已經push
團隊協(xié)作時受楼,有一種很常見的錯誤垦搬,自己提交倉庫時,忘了提交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
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目錄中圈浇。