天天看點

Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

背景

我獨立開發了《聯機桌遊合集》,是個網頁,可以很友善的跟朋友聯機玩鬥地主、五子棋、象棋等遊戲。這些遊戲是不同的前端項目,而這些項目有很多公共依賴,我是如何管理的呢?

我使用的方案中,Git Submodules 承擔着非常重要的角色。今天先給大家介紹下 Git Submodules,以後我會給大家更詳細的介紹如何管理不同項目的公共依賴。會寫在專欄《前端入門到進階》,歡迎關注。

為什麼你值得讀這篇文章?

  • 一些技術部落格寫的很詳細,但不适合新人學習。
  • 官方文檔很全面,适合了解詳細指令,但主次不分明。

本文會根據我的大量的 submodules 實踐經驗(包括工作和個人開發),隻解釋常用的指令。當你了解這些指令,你完全可以像我一樣使用 Git Submodules。

為什麼有 submodules?

  1. 解決公共代碼問題。如果某些檔案,在項目A和項目B中都會用到,例如元件庫,那麼這些檔案可以作為 submodules 來管理,減少重複代碼。(當然,該場景下npm包是另一解決方案,你需要選擇一種方案。)
  2. 解決團隊維護難題。如果一個大項目是一個大 Git 倉庫,需要統一編譯,不同的子產品由不同團隊維護,放在同一個 Git 倉庫有諸多難處:例如多個團隊的 MR 混在一起、權限難以區分等。這種情況即使公司内網 Git 權限做的足夠精細,倉庫管理者的學習成本也會很高,很難深度使用這種進階功能。為了解決多團隊維護的難題,Git Submodules 也能大展身手,它可以讓每個團隊負責的子產品就是一個 Git 倉庫,這些 Git 倉庫都被包含在同一個主 Git 項目下。(當然,微前端、微服務是另一種解決方案,你需要選擇一種方案。)

了解 Git Submodules

有2個概念:主項目、submodule(子子產品)。這兩者各自都是完整的 Git 倉庫。

如何讓一個Git倉庫變為另一個Git倉庫的 submodule

  1. 建立Git倉庫A。
  2. 建立Git倉庫B。
  3. 在Git倉庫A中,通過​

    ​git submodule add ...(倉庫B的位址,即git clone時後面那串東西)​

    ​​,可以把倉庫B當作倉庫A的submodule,此時A就成了主項目。【注:B也可以做A的主項目,通過在倉庫B執行​

    ​git submodule add ...(A位址)​

    ​即可,因為二者都是完整Git倉庫,在建立父子關系前,沒有差異的。】

注意事項

  • 執行操作後,會在目前父項目下建立個檔案夾,名字就是 submodule 倉庫的名字。這個檔案夾裡面的内容,是 submodule 對應 Git 倉庫的完整代碼。
  • 如果你希望換個名字,或者換個路徑(例如放在某個更深的目錄下),也是允許的,需要後面增加個路徑參數,例如​

    ​git submodule add ...(倉庫位址) src/B(你希望 submodule 位于的檔案夾路徑)​

submodule 的父子關系存在哪裡

關系是儲存在主項目的 Git 倉庫中。

被當作 submodule 的 Git 倉庫,其實不知道自己變成了 submodule,它更不知道爸爸們有誰。(意思是,當你打開某個被當作 submodule 的 Git 倉庫首頁時,或者拉下這個倉庫時,沒有任何痕迹表明它是個submodule。因為父子資訊不存在這裡,隻存在爸爸那裡。)

submodule 的父子關系資訊怎麼存

.gitmodules 檔案

父子關系的資訊儲存在主項目的 ​

​.gitmodules​

​ 檔案,如果不是新加 submodule,這個檔案通常不必改變了,因為資訊比較固定。

Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

submodule 的版本号

主項目還儲存了對應 submodule 的版本号(commit id),沒有備援存儲 submodule 的代碼。

Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

可以看到,這其實是個跳轉到另一個倉庫的連結,指明了具體的 commit id。

這個版本号,是需要經常變更的。

submodule 開發常用操作

如何給 submodule 倉庫送出更新

方法一,像普通倉庫一樣更新

之前說過,submodule 倉庫也是個普通的 Git 倉庫,它并不知道自己有多少個爸爸。

我們可以直接​

​git clone xxx​

​這個倉庫,像給普通 Git 倉庫送出更新一樣,去更新它。

方法二,在主項目中更新

例如主項目在檔案夾​

​A​

​,A裡面包含:

  • ​.git​

    ​檔案夾
  • ​READMD.md​

    ​主項目的ReadMe檔案。
  • ​B​

    ​檔案夾,是個 submodule。

我們可以進入B檔案夾​

​cd B​

​​,你會發現在B中,也可以執行​

​git status​

​​等指令,此時的​

​git​

​指令都會是針對倉庫B的,你可以在這裡切換分支、送出更新,這時候,送出的都是submodule的變更。

這是更推薦的方式。因為很多時候 submodule 是對主項目倉庫有依賴的。可能它是個元件庫,沒有運作環境,需要在主項目中debug。這點非常友善!

注意事項

當你在檔案夾B中做commit後,檔案夾B裡面就有了新的 commit id。此時主項目A中所記錄的 submodule 的commit id也會更新。是以,你​

​cd ..​

​回到檔案夾A後,會發現A有變更了,變更内容是:舊commid id變成了新的commit id。

Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

下面是​

​git diff​

​:

Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

如何在主項目倉庫,拉取 submodule 的更新

方法一,cd submodule 後 git pull

在 submodule 中,所有git操作就當作一個普通的 Git 倉庫就行,你可以切換分支、送出代碼、拉取更新等。

這個方法,你可以拉取到 submodule 的master最新代碼。但是如果這時候的commit id跟主項目裡記錄的 submodule 的 commit id 不一緻,你會在主項目倉庫看到diff,你可能需要送出主項目更新。

方法二,主項目執行git submodule update --remote [submodule檔案夾相對路徑]

這個方法會自動拉取submodule的主分支(通常叫master或main)的最新版本。效果跟方法一一緻。

如果你不帶參數​

​[submodule檔案夾相對路徑]​

​,就會更新所有 submodules。

注意事項,更新後需送出主項目變更。

當我們更新子項目後,相當于是把主項目記錄的 submodule 的 commit id 給更新了,需要送出下主項目的變更。

方法三,主項目執行 git submodule update [submodule檔案夾相對路徑]

注意,這個方法會使 submodule 的分支處于主項目裡指定的 commit id。可能并不是拉 submodule 的 master 最新代碼。

是以,這種方法僅适用于,當主倉庫裡記錄的 submodule 的 commit id 已經是最新的(可能被其他同僚送出過)。或者你期望 submodule 跟主倉庫記錄的保持一緻時,也可以使用該方法。

如何 clone 包含 submodule 的倉庫

方法一,按需clone submodule

  1. 先​

    ​git clone 主項目倉庫​

    ​并進入主項目檔案夾,這時候submodule的檔案夾都是空的。
  2. 執行​

    ​git submodule init [submodule的檔案夾的相對路徑]​

    ​。
  3. 執行​

    ​git submodule update [submodule的檔案夾的相對路徑]​

    ​。

這就按需clone了submodule。什麼時候有用呢?跨團隊協作某個主項目時,一些其它團隊的submodule我們沒必要安裝,就不必執行​

​init​

​​和​

​update​

​了。

合并第2、3步驟

第2、3步可以合并。使用以下指令:

git submodule update --init [submodule的檔案夾的相對路徑]      

注意順序,​

​--init​

​​跟​

​[submodule的檔案夾的相對路徑]​

​的位置不可以調換噢。

Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

方法二,一次性clone所有 submodule

  1. 先​

    ​git clone 主項目倉庫​

    ​,這時候submodule的檔案夾都是空的。
  2. 執行​

    ​git submodule init​

    ​。
  3. 執行​

    ​git submodule update​

    ​。

隻要不寫submodule,那麼就一次性檢查該主項目的所有submodule,都拉下來。

Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

合并第2、3步驟

git submodule update --init      

合并第1、2、3步驟

git clone --recurse-submodules [主項目Git倉庫位址]      
Git Submodules 介紹(通俗易懂,總結了工作完全夠用的 submodule 指令)

如何建立 submodule 關系

cd到主項目,執行:

git submodule add ...(另一倉庫位址,即git clone時後面那串東西)      

下面可以指定 submodule 放到哪個子檔案夾

git submodule add ...(另一倉庫位址) [(可選,submodule下載下傳的路徑)]      

更多資料

通過官方文檔,你可以了解到更多場景,但是我從來沒使用過其它場景了,因為用不到。本文描述的完全滿足了我所有日常使用場景。

而進階場景會導緻協作變困難,因為不是所有開發者都懂這些更複雜的指令和配置:

  • 嵌套的submodule怎麼快速拉取?(就是有個父項目有兒子、也有孫子、還有祖孫子等等,通過​

    ​--recurse-submodules​

    ​​或​

    ​--recursive​

    ​參數)。
  • 通過配置​

    ​git config -f .gitmodules submodule.子子產品檔案夾相對目錄.branch 子子產品分支名​

    ​​,使得每次執行​

    ​git submodule update --remote​

    ​時,追蹤任意指定的子子產品分支(而非預設的主分支master)。
  • 通過​

    ​foreach​

    ​指令可以友善的給所有子子產品依次執行一樣的指令。
  • 英文:www.git-scm.com/book/en/v2/…
  • 中文:git-scm.com/book/zh/v2/…

寫在最後